001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.activemq.broker.region;
018
019import java.io.IOException;
020import java.util.ArrayList;
021import java.util.Collection;
022import java.util.Collections;
023import java.util.Comparator;
024import java.util.HashSet;
025import java.util.Iterator;
026import java.util.LinkedHashMap;
027import java.util.LinkedHashSet;
028import java.util.LinkedList;
029import java.util.List;
030import java.util.Map;
031import java.util.Set;
032import java.util.concurrent.CancellationException;
033import java.util.concurrent.ConcurrentLinkedQueue;
034import java.util.concurrent.CountDownLatch;
035import java.util.concurrent.DelayQueue;
036import java.util.concurrent.Delayed;
037import java.util.concurrent.ExecutorService;
038import java.util.concurrent.TimeUnit;
039import java.util.concurrent.atomic.AtomicBoolean;
040import java.util.concurrent.atomic.AtomicLong;
041import java.util.concurrent.locks.Lock;
042import java.util.concurrent.locks.ReentrantLock;
043import java.util.concurrent.locks.ReentrantReadWriteLock;
044
045import javax.jms.InvalidSelectorException;
046import javax.jms.JMSException;
047import javax.jms.ResourceAllocationException;
048
049import org.apache.activemq.broker.BrokerService;
050import org.apache.activemq.broker.ConnectionContext;
051import org.apache.activemq.broker.ProducerBrokerExchange;
052import org.apache.activemq.broker.region.cursors.OrderedPendingList;
053import org.apache.activemq.broker.region.cursors.PendingList;
054import org.apache.activemq.broker.region.cursors.PendingMessageCursor;
055import org.apache.activemq.broker.region.cursors.PrioritizedPendingList;
056import org.apache.activemq.broker.region.cursors.StoreQueueCursor;
057import org.apache.activemq.broker.region.cursors.VMPendingMessageCursor;
058import org.apache.activemq.broker.region.group.CachedMessageGroupMapFactory;
059import org.apache.activemq.broker.region.group.MessageGroupMap;
060import org.apache.activemq.broker.region.group.MessageGroupMapFactory;
061import org.apache.activemq.broker.region.policy.DeadLetterStrategy;
062import org.apache.activemq.broker.region.policy.DispatchPolicy;
063import org.apache.activemq.broker.region.policy.RoundRobinDispatchPolicy;
064import org.apache.activemq.broker.util.InsertionCountList;
065import org.apache.activemq.command.ActiveMQDestination;
066import org.apache.activemq.command.ConsumerId;
067import org.apache.activemq.command.ExceptionResponse;
068import org.apache.activemq.command.Message;
069import org.apache.activemq.command.MessageAck;
070import org.apache.activemq.command.MessageDispatchNotification;
071import org.apache.activemq.command.MessageId;
072import org.apache.activemq.command.ProducerAck;
073import org.apache.activemq.command.ProducerInfo;
074import org.apache.activemq.command.Response;
075import org.apache.activemq.filter.BooleanExpression;
076import org.apache.activemq.filter.MessageEvaluationContext;
077import org.apache.activemq.filter.NonCachedMessageEvaluationContext;
078import org.apache.activemq.selector.SelectorParser;
079import org.apache.activemq.state.ProducerState;
080import org.apache.activemq.store.IndexListener;
081import org.apache.activemq.store.ListenableFuture;
082import org.apache.activemq.store.MessageRecoveryListener;
083import org.apache.activemq.store.MessageStore;
084import org.apache.activemq.thread.Task;
085import org.apache.activemq.thread.TaskRunner;
086import org.apache.activemq.thread.TaskRunnerFactory;
087import org.apache.activemq.transaction.Synchronization;
088import org.apache.activemq.usage.Usage;
089import org.apache.activemq.usage.UsageListener;
090import org.apache.activemq.util.BrokerSupport;
091import org.apache.activemq.util.ThreadPoolUtils;
092import org.slf4j.Logger;
093import org.slf4j.LoggerFactory;
094import org.slf4j.MDC;
095
096/**
097 * The Queue is a List of MessageEntry objects that are dispatched to matching
098 * subscriptions.
099 */
100public class Queue extends BaseDestination implements Task, UsageListener, IndexListener {
101    protected static final Logger LOG = LoggerFactory.getLogger(Queue.class);
102    protected final TaskRunnerFactory taskFactory;
103    protected TaskRunner taskRunner;
104    private final ReentrantReadWriteLock consumersLock = new ReentrantReadWriteLock();
105    protected final List<Subscription> consumers = new ArrayList<Subscription>(50);
106    private final ReentrantReadWriteLock messagesLock = new ReentrantReadWriteLock();
107    protected PendingMessageCursor messages;
108    private final ReentrantReadWriteLock pagedInMessagesLock = new ReentrantReadWriteLock();
109    private final PendingList pagedInMessages = new OrderedPendingList();
110    // Messages that are paged in but have not yet been targeted at a subscription
111    private final ReentrantReadWriteLock pagedInPendingDispatchLock = new ReentrantReadWriteLock();
112    protected PendingList pagedInPendingDispatch = new OrderedPendingList();
113    protected PendingList redeliveredWaitingDispatch = new OrderedPendingList();
114    private MessageGroupMap messageGroupOwners;
115    private DispatchPolicy dispatchPolicy = new RoundRobinDispatchPolicy();
116    private MessageGroupMapFactory messageGroupMapFactory = new CachedMessageGroupMapFactory();
117    final Lock sendLock = new ReentrantLock();
118    private ExecutorService executor;
119    private final Map<MessageId, Runnable> messagesWaitingForSpace = new LinkedHashMap<MessageId, Runnable>();
120    private boolean useConsumerPriority = true;
121    private boolean strictOrderDispatch = false;
122    private final QueueDispatchSelector dispatchSelector;
123    private boolean optimizedDispatch = false;
124    private boolean iterationRunning = false;
125    private boolean firstConsumer = false;
126    private int timeBeforeDispatchStarts = 0;
127    private int consumersBeforeDispatchStarts = 0;
128    private CountDownLatch consumersBeforeStartsLatch;
129    private final AtomicLong pendingWakeups = new AtomicLong();
130    private boolean allConsumersExclusiveByDefault = false;
131    private final AtomicBoolean started = new AtomicBoolean();
132
133    private boolean resetNeeded;
134
135    private final Runnable sendMessagesWaitingForSpaceTask = new Runnable() {
136        @Override
137        public void run() {
138            asyncWakeup();
139        }
140    };
141    private final Runnable expireMessagesTask = new Runnable() {
142        @Override
143        public void run() {
144            expireMessages();
145        }
146    };
147
148    private final Object iteratingMutex = new Object();
149
150    class TimeoutMessage implements Delayed {
151
152        Message message;
153        ConnectionContext context;
154        long trigger;
155
156        public TimeoutMessage(Message message, ConnectionContext context, long delay) {
157            this.message = message;
158            this.context = context;
159            this.trigger = System.currentTimeMillis() + delay;
160        }
161
162        @Override
163        public long getDelay(TimeUnit unit) {
164            long n = trigger - System.currentTimeMillis();
165            return unit.convert(n, TimeUnit.MILLISECONDS);
166        }
167
168        @Override
169        public int compareTo(Delayed delayed) {
170            long other = ((TimeoutMessage) delayed).trigger;
171            int returnValue;
172            if (this.trigger < other) {
173                returnValue = -1;
174            } else if (this.trigger > other) {
175                returnValue = 1;
176            } else {
177                returnValue = 0;
178            }
179            return returnValue;
180        }
181    }
182
183    DelayQueue<TimeoutMessage> flowControlTimeoutMessages = new DelayQueue<TimeoutMessage>();
184
185    class FlowControlTimeoutTask extends Thread {
186
187        @Override
188        public void run() {
189            TimeoutMessage timeout;
190            try {
191                while (true) {
192                    timeout = flowControlTimeoutMessages.take();
193                    if (timeout != null) {
194                        synchronized (messagesWaitingForSpace) {
195                            if (messagesWaitingForSpace.remove(timeout.message.getMessageId()) != null) {
196                                ExceptionResponse response = new ExceptionResponse(
197                                        new ResourceAllocationException(
198                                                "Usage Manager Memory Limit reached. Stopping producer ("
199                                                        + timeout.message.getProducerId()
200                                                        + ") to prevent flooding "
201                                                        + getActiveMQDestination().getQualifiedName()
202                                                        + "."
203                                                        + " See http://activemq.apache.org/producer-flow-control.html for more info"));
204                                response.setCorrelationId(timeout.message.getCommandId());
205                                timeout.context.getConnection().dispatchAsync(response);
206                            }
207                        }
208                    }
209                }
210            } catch (InterruptedException e) {
211                LOG.debug(getName() + "Producer Flow Control Timeout Task is stopping");
212            }
213        }
214    };
215
216    private final FlowControlTimeoutTask flowControlTimeoutTask = new FlowControlTimeoutTask();
217
218    private final Comparator<Subscription> orderedCompare = new Comparator<Subscription>() {
219
220        @Override
221        public int compare(Subscription s1, Subscription s2) {
222            // We want the list sorted in descending order
223            int val = s2.getConsumerInfo().getPriority() - s1.getConsumerInfo().getPriority();
224            if (val == 0 && messageGroupOwners != null) {
225                // then ascending order of assigned message groups to favour less loaded consumers
226                // Long.compare in jdk7
227                long x = s1.getConsumerInfo().getAssignedGroupCount();
228                long y = s2.getConsumerInfo().getAssignedGroupCount();
229                val = (x < y) ? -1 : ((x == y) ? 0 : 1);
230            }
231            return val;
232        }
233    };
234
235    public Queue(BrokerService brokerService, final ActiveMQDestination destination, MessageStore store,
236            DestinationStatistics parentStats, TaskRunnerFactory taskFactory) throws Exception {
237        super(brokerService, store, destination, parentStats);
238        this.taskFactory = taskFactory;
239        this.dispatchSelector = new QueueDispatchSelector(destination);
240        if (store != null) {
241            store.registerIndexListener(this);
242        }
243    }
244
245    @Override
246    public List<Subscription> getConsumers() {
247        consumersLock.readLock().lock();
248        try {
249            return new ArrayList<Subscription>(consumers);
250        } finally {
251            consumersLock.readLock().unlock();
252        }
253    }
254
255    // make the queue easily visible in the debugger from its task runner
256    // threads
257    final class QueueThread extends Thread {
258        final Queue queue;
259
260        public QueueThread(Runnable runnable, String name, Queue queue) {
261            super(runnable, name);
262            this.queue = queue;
263        }
264    }
265
266    class BatchMessageRecoveryListener implements MessageRecoveryListener {
267        final LinkedList<Message> toExpire = new LinkedList<Message>();
268        final double totalMessageCount;
269        int recoveredAccumulator = 0;
270        int currentBatchCount;
271
272        BatchMessageRecoveryListener(int totalMessageCount) {
273            this.totalMessageCount = totalMessageCount;
274            currentBatchCount = recoveredAccumulator;
275        }
276
277        @Override
278        public boolean recoverMessage(Message message) {
279            recoveredAccumulator++;
280            if ((recoveredAccumulator % 10000) == 0) {
281                LOG.info("cursor for {} has recovered {} messages. {}% complete", new Object[]{ getActiveMQDestination().getQualifiedName(), recoveredAccumulator, new Integer((int) (recoveredAccumulator * 100 / totalMessageCount))});
282            }
283            // Message could have expired while it was being
284            // loaded..
285            if (message.isExpired() && broker.isExpired(message)) {
286                toExpire.add(message);
287                return true;
288            }
289            if (hasSpace()) {
290                message.setRegionDestination(Queue.this);
291                messagesLock.writeLock().lock();
292                try {
293                    try {
294                        messages.addMessageLast(message);
295                    } catch (Exception e) {
296                        LOG.error("Failed to add message to cursor", e);
297                    }
298                } finally {
299                    messagesLock.writeLock().unlock();
300                }
301                destinationStatistics.getMessages().increment();
302                return true;
303            }
304            return false;
305        }
306
307        @Override
308        public boolean recoverMessageReference(MessageId messageReference) throws Exception {
309            throw new RuntimeException("Should not be called.");
310        }
311
312        @Override
313        public boolean hasSpace() {
314            return true;
315        }
316
317        @Override
318        public boolean isDuplicate(MessageId id) {
319            return false;
320        }
321
322        public void reset() {
323            currentBatchCount = recoveredAccumulator;
324        }
325
326        public void processExpired() {
327            for (Message message: toExpire) {
328                messageExpired(createConnectionContext(), createMessageReference(message));
329                // drop message will decrement so counter
330                // balance here
331                destinationStatistics.getMessages().increment();
332            }
333            toExpire.clear();
334        }
335
336        public boolean done() {
337            return currentBatchCount == recoveredAccumulator;
338        }
339    }
340
341    @Override
342    public void setPrioritizedMessages(boolean prioritizedMessages) {
343        super.setPrioritizedMessages(prioritizedMessages);
344
345        if (prioritizedMessages && this.pagedInPendingDispatch instanceof OrderedPendingList) {
346            pagedInPendingDispatch = new PrioritizedPendingList();
347            redeliveredWaitingDispatch = new PrioritizedPendingList();
348        } else if(pagedInPendingDispatch instanceof PrioritizedPendingList) {
349            pagedInPendingDispatch = new OrderedPendingList();
350            redeliveredWaitingDispatch = new OrderedPendingList();
351        }
352    }
353
354    @Override
355    public void initialize() throws Exception {
356
357        if (this.messages == null) {
358            if (destination.isTemporary() || broker == null || store == null) {
359                this.messages = new VMPendingMessageCursor(isPrioritizedMessages());
360            } else {
361                this.messages = new StoreQueueCursor(broker, this);
362            }
363        }
364
365        // If a VMPendingMessageCursor don't use the default Producer System
366        // Usage
367        // since it turns into a shared blocking queue which can lead to a
368        // network deadlock.
369        // If we are cursoring to disk..it's not and issue because it does not
370        // block due
371        // to large disk sizes.
372        if (messages instanceof VMPendingMessageCursor) {
373            this.systemUsage = brokerService.getSystemUsage();
374            memoryUsage.setParent(systemUsage.getMemoryUsage());
375        }
376
377        this.taskRunner = taskFactory.createTaskRunner(this, "Queue:" + destination.getPhysicalName());
378
379        super.initialize();
380        if (store != null) {
381            // Restore the persistent messages.
382            messages.setSystemUsage(systemUsage);
383            messages.setEnableAudit(isEnableAudit());
384            messages.setMaxAuditDepth(getMaxAuditDepth());
385            messages.setMaxProducersToAudit(getMaxProducersToAudit());
386            messages.setUseCache(isUseCache());
387            messages.setMemoryUsageHighWaterMark(getCursorMemoryHighWaterMark());
388            final int messageCount = store.getMessageCount();
389            if (messageCount > 0 && messages.isRecoveryRequired()) {
390                BatchMessageRecoveryListener listener = new BatchMessageRecoveryListener(messageCount);
391                do {
392                   listener.reset();
393                   store.recoverNextMessages(getMaxPageSize(), listener);
394                   listener.processExpired();
395               } while (!listener.done());
396            } else {
397                destinationStatistics.getMessages().setCount(messageCount);
398            }
399        }
400    }
401
402    /*
403     * Holder for subscription that needs attention on next iterate browser
404     * needs access to existing messages in the queue that have already been
405     * dispatched
406     */
407    class BrowserDispatch {
408        QueueBrowserSubscription browser;
409
410        public BrowserDispatch(QueueBrowserSubscription browserSubscription) {
411            browser = browserSubscription;
412            browser.incrementQueueRef();
413        }
414
415        void done() {
416            try {
417                browser.decrementQueueRef();
418            } catch (Exception e) {
419                LOG.warn("decrement ref on browser: " + browser, e);
420            }
421        }
422
423        public QueueBrowserSubscription getBrowser() {
424            return browser;
425        }
426    }
427
428    ConcurrentLinkedQueue<BrowserDispatch> browserDispatches = new ConcurrentLinkedQueue<BrowserDispatch>();
429
430    @Override
431    public void addSubscription(ConnectionContext context, Subscription sub) throws Exception {
432        LOG.debug("{} add sub: {}, dequeues: {}, dispatched: {}, inflight: {}", new Object[]{ getActiveMQDestination().getQualifiedName(), sub, getDestinationStatistics().getDequeues().getCount(), getDestinationStatistics().getDispatched().getCount(), getDestinationStatistics().getInflight().getCount() });
433
434        super.addSubscription(context, sub);
435        // synchronize with dispatch method so that no new messages are sent
436        // while setting up a subscription. avoid out of order messages,
437        // duplicates, etc.
438        pagedInPendingDispatchLock.writeLock().lock();
439        try {
440
441            sub.add(context, this);
442
443            // needs to be synchronized - so no contention with dispatching
444            // consumersLock.
445            consumersLock.writeLock().lock();
446            try {
447                // set a flag if this is a first consumer
448                if (consumers.size() == 0) {
449                    firstConsumer = true;
450                    if (consumersBeforeDispatchStarts != 0) {
451                        consumersBeforeStartsLatch = new CountDownLatch(consumersBeforeDispatchStarts - 1);
452                    }
453                } else {
454                    if (consumersBeforeStartsLatch != null) {
455                        consumersBeforeStartsLatch.countDown();
456                    }
457                }
458
459                addToConsumerList(sub);
460                if (sub.getConsumerInfo().isExclusive() || isAllConsumersExclusiveByDefault()) {
461                    Subscription exclusiveConsumer = dispatchSelector.getExclusiveConsumer();
462                    if (exclusiveConsumer == null) {
463                        exclusiveConsumer = sub;
464                    } else if (sub.getConsumerInfo().getPriority() == Byte.MAX_VALUE ||
465                        sub.getConsumerInfo().getPriority() > exclusiveConsumer.getConsumerInfo().getPriority()) {
466                        exclusiveConsumer = sub;
467                    }
468                    dispatchSelector.setExclusiveConsumer(exclusiveConsumer);
469                }
470            } finally {
471                consumersLock.writeLock().unlock();
472            }
473
474            if (sub instanceof QueueBrowserSubscription) {
475                // tee up for dispatch in next iterate
476                QueueBrowserSubscription browserSubscription = (QueueBrowserSubscription) sub;
477                BrowserDispatch browserDispatch = new BrowserDispatch(browserSubscription);
478                browserDispatches.add(browserDispatch);
479            }
480
481            if (!this.optimizedDispatch) {
482                wakeup();
483            }
484        } finally {
485            pagedInPendingDispatchLock.writeLock().unlock();
486        }
487        if (this.optimizedDispatch) {
488            // Outside of dispatchLock() to maintain the lock hierarchy of
489            // iteratingMutex -> dispatchLock. - see
490            // https://issues.apache.org/activemq/browse/AMQ-1878
491            wakeup();
492        }
493    }
494
495    @Override
496    public void removeSubscription(ConnectionContext context, Subscription sub, long lastDeiveredSequenceId)
497            throws Exception {
498        super.removeSubscription(context, sub, lastDeiveredSequenceId);
499        // synchronize with dispatch method so that no new messages are sent
500        // while removing up a subscription.
501        pagedInPendingDispatchLock.writeLock().lock();
502        try {
503            LOG.debug("{} remove sub: {}, lastDeliveredSeqId: {}, dequeues: {}, dispatched: {}, inflight: {}, groups: {}", new Object[]{
504                    getActiveMQDestination().getQualifiedName(),
505                    sub,
506                    lastDeiveredSequenceId,
507                    getDestinationStatistics().getDequeues().getCount(),
508                    getDestinationStatistics().getDispatched().getCount(),
509                    getDestinationStatistics().getInflight().getCount(),
510                    sub.getConsumerInfo().getAssignedGroupCount()
511            });
512            consumersLock.writeLock().lock();
513            try {
514                removeFromConsumerList(sub);
515                if (sub.getConsumerInfo().isExclusive()) {
516                    Subscription exclusiveConsumer = dispatchSelector.getExclusiveConsumer();
517                    if (exclusiveConsumer == sub) {
518                        exclusiveConsumer = null;
519                        for (Subscription s : consumers) {
520                            if (s.getConsumerInfo().isExclusive()
521                                    && (exclusiveConsumer == null || s.getConsumerInfo().getPriority() > exclusiveConsumer
522                                            .getConsumerInfo().getPriority())) {
523                                exclusiveConsumer = s;
524
525                            }
526                        }
527                        dispatchSelector.setExclusiveConsumer(exclusiveConsumer);
528                    }
529                } else if (isAllConsumersExclusiveByDefault()) {
530                    Subscription exclusiveConsumer = null;
531                    for (Subscription s : consumers) {
532                        if (exclusiveConsumer == null
533                                || s.getConsumerInfo().getPriority() > exclusiveConsumer
534                                .getConsumerInfo().getPriority()) {
535                            exclusiveConsumer = s;
536                                }
537                    }
538                    dispatchSelector.setExclusiveConsumer(exclusiveConsumer);
539                }
540                ConsumerId consumerId = sub.getConsumerInfo().getConsumerId();
541                getMessageGroupOwners().removeConsumer(consumerId);
542
543                // redeliver inflight messages
544
545                boolean markAsRedelivered = false;
546                MessageReference lastDeliveredRef = null;
547                List<MessageReference> unAckedMessages = sub.remove(context, this);
548
549                // locate last redelivered in unconsumed list (list in delivery rather than seq order)
550                if (lastDeiveredSequenceId > 0) {
551                    for (MessageReference ref : unAckedMessages) {
552                        if (ref.getMessageId().getBrokerSequenceId() == lastDeiveredSequenceId) {
553                            lastDeliveredRef = ref;
554                            markAsRedelivered = true;
555                            LOG.debug("found lastDeliveredSeqID: {}, message reference: {}", lastDeiveredSequenceId, ref.getMessageId());
556                            break;
557                        }
558                    }
559                }
560
561                for (MessageReference ref : unAckedMessages) {
562                    // AMQ-5107: don't resend if the broker is shutting down
563                    if ( this.brokerService.isStopping() ) {
564                        break;
565                    }
566                    QueueMessageReference qmr = (QueueMessageReference) ref;
567                    if (qmr.getLockOwner() == sub) {
568                        qmr.unlock();
569
570                        // have no delivery information
571                        if (lastDeiveredSequenceId == 0) {
572                            qmr.incrementRedeliveryCounter();
573                        } else {
574                            if (markAsRedelivered) {
575                                qmr.incrementRedeliveryCounter();
576                            }
577                            if (ref == lastDeliveredRef) {
578                                // all that follow were not redelivered
579                                markAsRedelivered = false;
580                            }
581                        }
582                    }
583                    if (!qmr.isDropped()) {
584                        redeliveredWaitingDispatch.addMessageLast(qmr);
585                    }
586                }
587                if (sub instanceof QueueBrowserSubscription) {
588                    ((QueueBrowserSubscription)sub).decrementQueueRef();
589                    browserDispatches.remove(sub);
590                }
591                // AMQ-5107: don't resend if the broker is shutting down
592                if (!redeliveredWaitingDispatch.isEmpty() && (! this.brokerService.isStopping())) {
593                    doDispatch(new OrderedPendingList());
594                }
595            } finally {
596                consumersLock.writeLock().unlock();
597            }
598            if (!this.optimizedDispatch) {
599                wakeup();
600            }
601        } finally {
602            pagedInPendingDispatchLock.writeLock().unlock();
603        }
604        if (this.optimizedDispatch) {
605            // Outside of dispatchLock() to maintain the lock hierarchy of
606            // iteratingMutex -> dispatchLock. - see
607            // https://issues.apache.org/activemq/browse/AMQ-1878
608            wakeup();
609        }
610    }
611
612    @Override
613    public void send(final ProducerBrokerExchange producerExchange, final Message message) throws Exception {
614        final ConnectionContext context = producerExchange.getConnectionContext();
615        // There is delay between the client sending it and it arriving at the
616        // destination.. it may have expired.
617        message.setRegionDestination(this);
618        ProducerState state = producerExchange.getProducerState();
619        if (state == null) {
620            LOG.warn("Send failed for: {}, missing producer state for: {}", message, producerExchange);
621            throw new JMSException("Cannot send message to " + getActiveMQDestination() + " with invalid (null) producer state");
622        }
623        final ProducerInfo producerInfo = producerExchange.getProducerState().getInfo();
624        final boolean sendProducerAck = !message.isResponseRequired() && producerInfo.getWindowSize() > 0
625                && !context.isInRecoveryMode();
626        if (message.isExpired()) {
627            // message not stored - or added to stats yet - so chuck here
628            broker.getRoot().messageExpired(context, message, null);
629            if (sendProducerAck) {
630                ProducerAck ack = new ProducerAck(producerInfo.getProducerId(), message.getSize());
631                context.getConnection().dispatchAsync(ack);
632            }
633            return;
634        }
635        if (memoryUsage.isFull()) {
636            isFull(context, memoryUsage);
637            fastProducer(context, producerInfo);
638            if (isProducerFlowControl() && context.isProducerFlowControl()) {
639                if (warnOnProducerFlowControl) {
640                    warnOnProducerFlowControl = false;
641                    LOG.info("Usage Manager Memory Limit ({}) reached on {}, size {}. Producers will be throttled to the rate at which messages are removed from this destination to prevent flooding it. See http://activemq.apache.org/producer-flow-control.html for more info.",
642                                    memoryUsage.getLimit(), getActiveMQDestination().getQualifiedName(), destinationStatistics.getMessages().getCount());
643                }
644
645                if (!context.isNetworkConnection() && systemUsage.isSendFailIfNoSpace()) {
646                    throw new ResourceAllocationException("Usage Manager Memory Limit reached. Stopping producer ("
647                            + message.getProducerId() + ") to prevent flooding "
648                            + getActiveMQDestination().getQualifiedName() + "."
649                            + " See http://activemq.apache.org/producer-flow-control.html for more info");
650                }
651
652                // We can avoid blocking due to low usage if the producer is
653                // sending
654                // a sync message or if it is using a producer window
655                if (producerInfo.getWindowSize() > 0 || message.isResponseRequired()) {
656                    // copy the exchange state since the context will be
657                    // modified while we are waiting
658                    // for space.
659                    final ProducerBrokerExchange producerExchangeCopy = producerExchange.copy();
660                    synchronized (messagesWaitingForSpace) {
661                     // Start flow control timeout task
662                        // Prevent trying to start it multiple times
663                        if (!flowControlTimeoutTask.isAlive()) {
664                            flowControlTimeoutTask.setName(getName()+" Producer Flow Control Timeout Task");
665                            flowControlTimeoutTask.start();
666                        }
667                        messagesWaitingForSpace.put(message.getMessageId(), new Runnable() {
668                            @Override
669                            public void run() {
670
671                                try {
672                                    // While waiting for space to free up... the
673                                    // message may have expired.
674                                    if (message.isExpired()) {
675                                        LOG.error("expired waiting for space..");
676                                        broker.messageExpired(context, message, null);
677                                        destinationStatistics.getExpired().increment();
678                                    } else {
679                                        doMessageSend(producerExchangeCopy, message);
680                                    }
681
682                                    if (sendProducerAck) {
683                                        ProducerAck ack = new ProducerAck(producerInfo.getProducerId(), message
684                                                .getSize());
685                                        context.getConnection().dispatchAsync(ack);
686                                    } else {
687                                        Response response = new Response();
688                                        response.setCorrelationId(message.getCommandId());
689                                        context.getConnection().dispatchAsync(response);
690                                    }
691
692                                } catch (Exception e) {
693                                    if (!sendProducerAck && !context.isInRecoveryMode() && !brokerService.isStopping()) {
694                                        ExceptionResponse response = new ExceptionResponse(e);
695                                        response.setCorrelationId(message.getCommandId());
696                                        context.getConnection().dispatchAsync(response);
697                                    } else {
698                                        LOG.debug("unexpected exception on deferred send of: {}", message, e);
699                                    }
700                                }
701                            }
702                        });
703
704                        if (!context.isNetworkConnection() && systemUsage.getSendFailIfNoSpaceAfterTimeout() != 0) {
705                            flowControlTimeoutMessages.add(new TimeoutMessage(message, context, systemUsage
706                                    .getSendFailIfNoSpaceAfterTimeout()));
707                        }
708
709                        registerCallbackForNotFullNotification();
710                        context.setDontSendReponse(true);
711                        return;
712                    }
713
714                } else {
715
716                    if (memoryUsage.isFull()) {
717                        waitForSpace(context, producerExchange, memoryUsage, "Usage Manager Memory Limit reached. Producer ("
718                                + message.getProducerId() + ") stopped to prevent flooding "
719                                + getActiveMQDestination().getQualifiedName() + "."
720                                + " See http://activemq.apache.org/producer-flow-control.html for more info");
721                    }
722
723                    // The usage manager could have delayed us by the time
724                    // we unblock the message could have expired..
725                    if (message.isExpired()) {
726                        LOG.debug("Expired message: {}", message);
727                        broker.getRoot().messageExpired(context, message, null);
728                        return;
729                    }
730                }
731            }
732        }
733        doMessageSend(producerExchange, message);
734        if (sendProducerAck) {
735            ProducerAck ack = new ProducerAck(producerInfo.getProducerId(), message.getSize());
736            context.getConnection().dispatchAsync(ack);
737        }
738    }
739
740    private void registerCallbackForNotFullNotification() {
741        // If the usage manager is not full, then the task will not
742        // get called..
743        if (!memoryUsage.notifyCallbackWhenNotFull(sendMessagesWaitingForSpaceTask)) {
744            // so call it directly here.
745            sendMessagesWaitingForSpaceTask.run();
746        }
747    }
748
749    private final LinkedList<MessageContext> indexOrderedCursorUpdates = new LinkedList<>();
750
751    @Override
752    public void onAdd(MessageContext messageContext) {
753        synchronized (indexOrderedCursorUpdates) {
754            indexOrderedCursorUpdates.addLast(messageContext);
755        }
756    }
757
758    private void doPendingCursorAdditions() throws Exception {
759        LinkedList<MessageContext> orderedUpdates = new LinkedList<>();
760        sendLock.lockInterruptibly();
761        try {
762            synchronized (indexOrderedCursorUpdates) {
763                MessageContext candidate = indexOrderedCursorUpdates.peek();
764                while (candidate != null && candidate.message.getMessageId().getFutureOrSequenceLong() != null) {
765                    candidate = indexOrderedCursorUpdates.removeFirst();
766                    // check for duplicate adds suppressed by the store
767                    if (candidate.message.getMessageId().getFutureOrSequenceLong() instanceof Long && ((Long)candidate.message.getMessageId().getFutureOrSequenceLong()).compareTo(-1l) == 0) {
768                        LOG.warn("{} messageStore indicated duplicate add attempt for {}, suppressing duplicate dispatch", this, candidate.message.getMessageId());
769                    } else {
770                        orderedUpdates.add(candidate);
771                    }
772                    candidate = indexOrderedCursorUpdates.peek();
773                }
774            }
775            messagesLock.writeLock().lock();
776            try {
777                for (MessageContext messageContext : orderedUpdates) {
778                    if (!messages.addMessageLast(messageContext.message)) {
779                        // cursor suppressed a duplicate
780                        messageContext.duplicate = true;
781                    }
782                    if (messageContext.onCompletion != null) {
783                        messageContext.onCompletion.run();
784                    }
785                }
786            } finally {
787                messagesLock.writeLock().unlock();
788            }
789        } finally {
790            sendLock.unlock();
791        }
792        for (MessageContext messageContext : orderedUpdates) {
793            if (!messageContext.duplicate) {
794                messageSent(messageContext.context, messageContext.message);
795            }
796        }
797        orderedUpdates.clear();
798    }
799
800    final class CursorAddSync extends Synchronization {
801
802        private final MessageContext messageContext;
803
804        CursorAddSync(MessageContext messageContext) {
805            this.messageContext = messageContext;
806            this.messageContext.message.incrementReferenceCount();
807        }
808
809        @Override
810        public void afterCommit() throws Exception {
811            if (store != null && messageContext.message.isPersistent()) {
812                doPendingCursorAdditions();
813            } else {
814                cursorAdd(messageContext.message);
815                messageSent(messageContext.context, messageContext.message);
816            }
817            messageContext.message.decrementReferenceCount();
818        }
819
820        @Override
821        public void afterRollback() throws Exception {
822            messageContext.message.decrementReferenceCount();
823        }
824    }
825
826    void doMessageSend(final ProducerBrokerExchange producerExchange, final Message message) throws IOException,
827            Exception {
828        final ConnectionContext context = producerExchange.getConnectionContext();
829        ListenableFuture<Object> result = null;
830
831        producerExchange.incrementSend();
832        checkUsage(context, producerExchange, message);
833        sendLock.lockInterruptibly();
834        try {
835            if (store != null && message.isPersistent()) {
836                try {
837                    message.getMessageId().setBrokerSequenceId(getDestinationSequenceId());
838                    if (messages.isCacheEnabled()) {
839                        result = store.asyncAddQueueMessage(context, message, isOptimizeStorage());
840                        result.addListener(new PendingMarshalUsageTracker(message));
841                    } else {
842                        store.addMessage(context, message);
843                    }
844                    if (isReduceMemoryFootprint()) {
845                        message.clearMarshalledState();
846                    }
847                } catch (Exception e) {
848                    // we may have a store in inconsistent state, so reset the cursor
849                    // before restarting normal broker operations
850                    resetNeeded = true;
851                    throw e;
852                }
853            }
854            orderedCursorAdd(message, context);
855        } finally {
856            sendLock.unlock();
857        }
858        if (store == null || (!context.isInTransaction() && !message.isPersistent())) {
859            messageSent(context, message);
860        }
861        if (result != null && message.isResponseRequired() && !result.isCancelled()) {
862            try {
863                result.get();
864            } catch (CancellationException e) {
865                // ignore - the task has been cancelled if the message
866                // has already been deleted
867            }
868        }
869    }
870
871    private void orderedCursorAdd(Message message, ConnectionContext context) throws Exception {
872        if (context.isInTransaction()) {
873            context.getTransaction().addSynchronization(new CursorAddSync(new MessageContext(context, message, null)));
874        } else if (store != null && message.isPersistent()) {
875            doPendingCursorAdditions();
876        } else {
877            // no ordering issue with non persistent messages
878            cursorAdd(message);
879        }
880    }
881
882    private void checkUsage(ConnectionContext context,ProducerBrokerExchange producerBrokerExchange, Message message) throws ResourceAllocationException, IOException, InterruptedException {
883        if (message.isPersistent()) {
884            if (store != null && systemUsage.getStoreUsage().isFull(getStoreUsageHighWaterMark())) {
885                final String logMessage = "Persistent store is Full, " + getStoreUsageHighWaterMark() + "% of "
886                    + systemUsage.getStoreUsage().getLimit() + ". Stopping producer ("
887                    + message.getProducerId() + ") to prevent flooding "
888                    + getActiveMQDestination().getQualifiedName() + "."
889                    + " See http://activemq.apache.org/producer-flow-control.html for more info";
890
891                waitForSpace(context, producerBrokerExchange, systemUsage.getStoreUsage(), getStoreUsageHighWaterMark(), logMessage);
892            }
893        } else if (messages.getSystemUsage() != null && systemUsage.getTempUsage().isFull()) {
894            final String logMessage = "Temp Store is Full ("
895                    + systemUsage.getTempUsage().getPercentUsage() + "% of " + systemUsage.getTempUsage().getLimit()
896                    +"). Stopping producer (" + message.getProducerId()
897                + ") to prevent flooding " + getActiveMQDestination().getQualifiedName() + "."
898                + " See http://activemq.apache.org/producer-flow-control.html for more info";
899
900            waitForSpace(context, producerBrokerExchange, messages.getSystemUsage().getTempUsage(), logMessage);
901        }
902    }
903
904    private void expireMessages() {
905        LOG.debug("{} expiring messages ..", getActiveMQDestination().getQualifiedName());
906
907        // just track the insertion count
908        List<Message> browsedMessages = new InsertionCountList<Message>();
909        doBrowse(browsedMessages, this.getMaxExpirePageSize());
910        asyncWakeup();
911        LOG.debug("{} expiring messages done.", getActiveMQDestination().getQualifiedName());
912    }
913
914    @Override
915    public void gc() {
916    }
917
918    @Override
919    public void acknowledge(ConnectionContext context, Subscription sub, MessageAck ack, MessageReference node)
920            throws IOException {
921        messageConsumed(context, node);
922        if (store != null && node.isPersistent()) {
923            store.removeAsyncMessage(context, convertToNonRangedAck(ack, node));
924        }
925    }
926
927    Message loadMessage(MessageId messageId) throws IOException {
928        Message msg = null;
929        if (store != null) { // can be null for a temp q
930            msg = store.getMessage(messageId);
931            if (msg != null) {
932                msg.setRegionDestination(this);
933            }
934        }
935        return msg;
936    }
937
938    @Override
939    public String toString() {
940        return destination.getQualifiedName() + ", subscriptions=" + consumers.size()
941                + ", memory=" + memoryUsage.getPercentUsage() + "%, size=" + destinationStatistics.getMessages().getCount() + ", pending="
942                + indexOrderedCursorUpdates.size();
943    }
944
945    @Override
946    public void start() throws Exception {
947        if (started.compareAndSet(false, true)) {
948            if (memoryUsage != null) {
949                memoryUsage.start();
950            }
951            if (systemUsage.getStoreUsage() != null) {
952                systemUsage.getStoreUsage().start();
953            }
954            systemUsage.getMemoryUsage().addUsageListener(this);
955            messages.start();
956            if (getExpireMessagesPeriod() > 0) {
957                scheduler.executePeriodically(expireMessagesTask, getExpireMessagesPeriod());
958            }
959            doPageIn(false);
960        }
961    }
962
963    @Override
964    public void stop() throws Exception {
965        if (started.compareAndSet(true, false)) {
966            if (taskRunner != null) {
967                taskRunner.shutdown();
968            }
969            if (this.executor != null) {
970                ThreadPoolUtils.shutdownNow(executor);
971                executor = null;
972            }
973
974            scheduler.cancel(expireMessagesTask);
975
976            if (flowControlTimeoutTask.isAlive()) {
977                flowControlTimeoutTask.interrupt();
978            }
979
980            if (messages != null) {
981                messages.stop();
982            }
983
984            for (MessageReference messageReference : pagedInMessages.values()) {
985                messageReference.decrementReferenceCount();
986            }
987            pagedInMessages.clear();
988
989            systemUsage.getMemoryUsage().removeUsageListener(this);
990            if (memoryUsage != null) {
991                memoryUsage.stop();
992            }
993            if (store != null) {
994                store.stop();
995            }
996        }
997    }
998
999    // Properties
1000    // -------------------------------------------------------------------------
1001    @Override
1002    public ActiveMQDestination getActiveMQDestination() {
1003        return destination;
1004    }
1005
1006    public MessageGroupMap getMessageGroupOwners() {
1007        if (messageGroupOwners == null) {
1008            messageGroupOwners = getMessageGroupMapFactory().createMessageGroupMap();
1009            messageGroupOwners.setDestination(this);
1010        }
1011        return messageGroupOwners;
1012    }
1013
1014    public DispatchPolicy getDispatchPolicy() {
1015        return dispatchPolicy;
1016    }
1017
1018    public void setDispatchPolicy(DispatchPolicy dispatchPolicy) {
1019        this.dispatchPolicy = dispatchPolicy;
1020    }
1021
1022    public MessageGroupMapFactory getMessageGroupMapFactory() {
1023        return messageGroupMapFactory;
1024    }
1025
1026    public void setMessageGroupMapFactory(MessageGroupMapFactory messageGroupMapFactory) {
1027        this.messageGroupMapFactory = messageGroupMapFactory;
1028    }
1029
1030    public PendingMessageCursor getMessages() {
1031        return this.messages;
1032    }
1033
1034    public void setMessages(PendingMessageCursor messages) {
1035        this.messages = messages;
1036    }
1037
1038    public boolean isUseConsumerPriority() {
1039        return useConsumerPriority;
1040    }
1041
1042    public void setUseConsumerPriority(boolean useConsumerPriority) {
1043        this.useConsumerPriority = useConsumerPriority;
1044    }
1045
1046    public boolean isStrictOrderDispatch() {
1047        return strictOrderDispatch;
1048    }
1049
1050    public void setStrictOrderDispatch(boolean strictOrderDispatch) {
1051        this.strictOrderDispatch = strictOrderDispatch;
1052    }
1053
1054    public boolean isOptimizedDispatch() {
1055        return optimizedDispatch;
1056    }
1057
1058    public void setOptimizedDispatch(boolean optimizedDispatch) {
1059        this.optimizedDispatch = optimizedDispatch;
1060    }
1061
1062    public int getTimeBeforeDispatchStarts() {
1063        return timeBeforeDispatchStarts;
1064    }
1065
1066    public void setTimeBeforeDispatchStarts(int timeBeforeDispatchStarts) {
1067        this.timeBeforeDispatchStarts = timeBeforeDispatchStarts;
1068    }
1069
1070    public int getConsumersBeforeDispatchStarts() {
1071        return consumersBeforeDispatchStarts;
1072    }
1073
1074    public void setConsumersBeforeDispatchStarts(int consumersBeforeDispatchStarts) {
1075        this.consumersBeforeDispatchStarts = consumersBeforeDispatchStarts;
1076    }
1077
1078    public void setAllConsumersExclusiveByDefault(boolean allConsumersExclusiveByDefault) {
1079        this.allConsumersExclusiveByDefault = allConsumersExclusiveByDefault;
1080    }
1081
1082    public boolean isAllConsumersExclusiveByDefault() {
1083        return allConsumersExclusiveByDefault;
1084    }
1085
1086    public boolean isResetNeeded() {
1087        return resetNeeded;
1088    }
1089
1090    // Implementation methods
1091    // -------------------------------------------------------------------------
1092    private QueueMessageReference createMessageReference(Message message) {
1093        QueueMessageReference result = new IndirectMessageReference(message);
1094        return result;
1095    }
1096
1097    @Override
1098    public Message[] browse() {
1099        List<Message> browseList = new ArrayList<Message>();
1100        doBrowse(browseList, getMaxBrowsePageSize());
1101        return browseList.toArray(new Message[browseList.size()]);
1102    }
1103
1104    public void doBrowse(List<Message> browseList, int max) {
1105        final ConnectionContext connectionContext = createConnectionContext();
1106        try {
1107            int maxPageInAttempts = 1;
1108            messagesLock.readLock().lock();
1109            try {
1110                maxPageInAttempts += (messages.size() / getMaxPageSize());
1111            } finally {
1112                messagesLock.readLock().unlock();
1113            }
1114
1115            while (shouldPageInMoreForBrowse(max) && maxPageInAttempts-- > 0) {
1116                pageInMessages(!memoryUsage.isFull(110));
1117            };
1118
1119            doBrowseList(browseList, max, redeliveredWaitingDispatch, pagedInPendingDispatchLock, connectionContext, "redeliveredWaitingDispatch");
1120            doBrowseList(browseList, max, pagedInPendingDispatch, pagedInPendingDispatchLock, connectionContext, "pagedInPendingDispatch");
1121            doBrowseList(browseList, max, pagedInMessages, pagedInMessagesLock, connectionContext, "pagedInMessages");
1122
1123            // we need a store iterator to walk messages on disk, independent of the cursor which is tracking
1124            // the next message batch
1125        } catch (Exception e) {
1126            LOG.error("Problem retrieving message for browse", e);
1127        }
1128    }
1129
1130    protected void doBrowseList(List<Message> browseList, int max, PendingList list, ReentrantReadWriteLock lock, ConnectionContext connectionContext, String name) throws Exception {
1131        List<MessageReference> toExpire = new ArrayList<MessageReference>();
1132        lock.readLock().lock();
1133        try {
1134            addAll(list.values(), browseList, max, toExpire);
1135        } finally {
1136            lock.readLock().unlock();
1137        }
1138        for (MessageReference ref : toExpire) {
1139            if (broker.isExpired(ref)) {
1140                LOG.debug("expiring from {}: {}", name, ref);
1141                messageExpired(connectionContext, ref);
1142            } else {
1143                lock.writeLock().lock();
1144                try {
1145                    list.remove(ref);
1146                } finally {
1147                    lock.writeLock().unlock();
1148                }
1149                ref.decrementReferenceCount();
1150            }
1151        }
1152    }
1153
1154    private boolean shouldPageInMoreForBrowse(int max) {
1155        int alreadyPagedIn = 0;
1156        pagedInMessagesLock.readLock().lock();
1157        try {
1158            alreadyPagedIn = pagedInMessages.size();
1159        } finally {
1160            pagedInMessagesLock.readLock().unlock();
1161        }
1162        int messagesInQueue = alreadyPagedIn;
1163        messagesLock.readLock().lock();
1164        try {
1165            messagesInQueue += messages.size();
1166        } finally {
1167            messagesLock.readLock().unlock();
1168        }
1169
1170        LOG.trace("max {}, alreadyPagedIn {}, messagesCount {}, memoryUsage {}%", new Object[]{max, alreadyPagedIn, messagesInQueue, memoryUsage.getPercentUsage()});
1171        return (alreadyPagedIn < max)
1172                && (alreadyPagedIn < messagesInQueue)
1173                && messages.hasSpace();
1174    }
1175
1176    private void addAll(Collection<? extends MessageReference> refs, List<Message> l, int max,
1177            List<MessageReference> toExpire) throws Exception {
1178        for (Iterator<? extends MessageReference> i = refs.iterator(); i.hasNext() && l.size() < max;) {
1179            QueueMessageReference ref = (QueueMessageReference) i.next();
1180            if (ref.isExpired() && (ref.getLockOwner() == null)) {
1181                toExpire.add(ref);
1182            } else if (l.contains(ref.getMessage()) == false) {
1183                l.add(ref.getMessage());
1184            }
1185        }
1186    }
1187
1188    public QueueMessageReference getMessage(String id) {
1189        MessageId msgId = new MessageId(id);
1190        pagedInMessagesLock.readLock().lock();
1191        try {
1192            QueueMessageReference ref = (QueueMessageReference)this.pagedInMessages.get(msgId);
1193            if (ref != null) {
1194                return ref;
1195            }
1196        } finally {
1197            pagedInMessagesLock.readLock().unlock();
1198        }
1199        messagesLock.writeLock().lock();
1200        try{
1201            try {
1202                messages.reset();
1203                while (messages.hasNext()) {
1204                    MessageReference mr = messages.next();
1205                    QueueMessageReference qmr = createMessageReference(mr.getMessage());
1206                    qmr.decrementReferenceCount();
1207                    messages.rollback(qmr.getMessageId());
1208                    if (msgId.equals(qmr.getMessageId())) {
1209                        return qmr;
1210                    }
1211                }
1212            } finally {
1213                messages.release();
1214            }
1215        }finally {
1216            messagesLock.writeLock().unlock();
1217        }
1218        return null;
1219    }
1220
1221    public void purge() throws Exception {
1222        ConnectionContext c = createConnectionContext();
1223        List<MessageReference> list = null;
1224        long originalMessageCount = this.destinationStatistics.getMessages().getCount();
1225        do {
1226            doPageIn(true, false);  // signal no expiry processing needed.
1227            pagedInMessagesLock.readLock().lock();
1228            try {
1229                list = new ArrayList<MessageReference>(pagedInMessages.values());
1230            }finally {
1231                pagedInMessagesLock.readLock().unlock();
1232            }
1233
1234            for (MessageReference ref : list) {
1235                try {
1236                    QueueMessageReference r = (QueueMessageReference) ref;
1237                    removeMessage(c, r);
1238                } catch (IOException e) {
1239                }
1240            }
1241            // don't spin/hang if stats are out and there is nothing left in the
1242            // store
1243        } while (!list.isEmpty() && this.destinationStatistics.getMessages().getCount() > 0);
1244
1245        if (this.destinationStatistics.getMessages().getCount() > 0) {
1246            LOG.warn("{} after purge of {} messages, message count stats report: {}", getActiveMQDestination().getQualifiedName(), originalMessageCount, this.destinationStatistics.getMessages().getCount());
1247        } else {
1248            LOG.info("{} purged of {} messages", getActiveMQDestination().getQualifiedName(), originalMessageCount);
1249        }
1250        gc();
1251        this.destinationStatistics.getMessages().setCount(0);
1252        getMessages().clear();
1253    }
1254
1255    @Override
1256    public void clearPendingMessages() {
1257        messagesLock.writeLock().lock();
1258        try {
1259            if (resetNeeded) {
1260                messages.gc();
1261                messages.reset();
1262                resetNeeded = false;
1263            } else {
1264                messages.rebase();
1265            }
1266            asyncWakeup();
1267        } finally {
1268            messagesLock.writeLock().unlock();
1269        }
1270    }
1271
1272    /**
1273     * Removes the message matching the given messageId
1274     */
1275    public boolean removeMessage(String messageId) throws Exception {
1276        return removeMatchingMessages(createMessageIdFilter(messageId), 1) > 0;
1277    }
1278
1279    /**
1280     * Removes the messages matching the given selector
1281     *
1282     * @return the number of messages removed
1283     */
1284    public int removeMatchingMessages(String selector) throws Exception {
1285        return removeMatchingMessages(selector, -1);
1286    }
1287
1288    /**
1289     * Removes the messages matching the given selector up to the maximum number
1290     * of matched messages
1291     *
1292     * @return the number of messages removed
1293     */
1294    public int removeMatchingMessages(String selector, int maximumMessages) throws Exception {
1295        return removeMatchingMessages(createSelectorFilter(selector), maximumMessages);
1296    }
1297
1298    /**
1299     * Removes the messages matching the given filter up to the maximum number
1300     * of matched messages
1301     *
1302     * @return the number of messages removed
1303     */
1304    public int removeMatchingMessages(MessageReferenceFilter filter, int maximumMessages) throws Exception {
1305        int movedCounter = 0;
1306        Set<MessageReference> set = new LinkedHashSet<MessageReference>();
1307        ConnectionContext context = createConnectionContext();
1308        do {
1309            doPageIn(true);
1310            pagedInMessagesLock.readLock().lock();
1311            try {
1312                set.addAll(pagedInMessages.values());
1313            } finally {
1314                pagedInMessagesLock.readLock().unlock();
1315            }
1316            List<MessageReference> list = new ArrayList<MessageReference>(set);
1317            for (MessageReference ref : list) {
1318                IndirectMessageReference r = (IndirectMessageReference) ref;
1319                if (filter.evaluate(context, r)) {
1320
1321                    removeMessage(context, r);
1322                    set.remove(r);
1323                    if (++movedCounter >= maximumMessages && maximumMessages > 0) {
1324                        return movedCounter;
1325                    }
1326                }
1327            }
1328        } while (set.size() < this.destinationStatistics.getMessages().getCount());
1329        return movedCounter;
1330    }
1331
1332    /**
1333     * Copies the message matching the given messageId
1334     */
1335    public boolean copyMessageTo(ConnectionContext context, String messageId, ActiveMQDestination dest)
1336            throws Exception {
1337        return copyMatchingMessages(context, createMessageIdFilter(messageId), dest, 1) > 0;
1338    }
1339
1340    /**
1341     * Copies the messages matching the given selector
1342     *
1343     * @return the number of messages copied
1344     */
1345    public int copyMatchingMessagesTo(ConnectionContext context, String selector, ActiveMQDestination dest)
1346            throws Exception {
1347        return copyMatchingMessagesTo(context, selector, dest, -1);
1348    }
1349
1350    /**
1351     * Copies the messages matching the given selector up to the maximum number
1352     * of matched messages
1353     *
1354     * @return the number of messages copied
1355     */
1356    public int copyMatchingMessagesTo(ConnectionContext context, String selector, ActiveMQDestination dest,
1357            int maximumMessages) throws Exception {
1358        return copyMatchingMessages(context, createSelectorFilter(selector), dest, maximumMessages);
1359    }
1360
1361    /**
1362     * Copies the messages matching the given filter up to the maximum number of
1363     * matched messages
1364     *
1365     * @return the number of messages copied
1366     */
1367    public int copyMatchingMessages(ConnectionContext context, MessageReferenceFilter filter, ActiveMQDestination dest,
1368            int maximumMessages) throws Exception {
1369        int movedCounter = 0;
1370        int count = 0;
1371        Set<MessageReference> set = new LinkedHashSet<MessageReference>();
1372        do {
1373            int oldMaxSize = getMaxPageSize();
1374            setMaxPageSize((int) this.destinationStatistics.getMessages().getCount());
1375            doPageIn(true);
1376            setMaxPageSize(oldMaxSize);
1377            pagedInMessagesLock.readLock().lock();
1378            try {
1379                set.addAll(pagedInMessages.values());
1380            } finally {
1381                pagedInMessagesLock.readLock().unlock();
1382            }
1383            List<MessageReference> list = new ArrayList<MessageReference>(set);
1384            for (MessageReference ref : list) {
1385                IndirectMessageReference r = (IndirectMessageReference) ref;
1386                if (filter.evaluate(context, r)) {
1387
1388                    r.incrementReferenceCount();
1389                    try {
1390                        Message m = r.getMessage();
1391                        BrokerSupport.resend(context, m, dest);
1392                        if (++movedCounter >= maximumMessages && maximumMessages > 0) {
1393                            return movedCounter;
1394                        }
1395                    } finally {
1396                        r.decrementReferenceCount();
1397                    }
1398                }
1399                count++;
1400            }
1401        } while (count < this.destinationStatistics.getMessages().getCount());
1402        return movedCounter;
1403    }
1404
1405    /**
1406     * Move a message
1407     *
1408     * @param context
1409     *            connection context
1410     * @param m
1411     *            QueueMessageReference
1412     * @param dest
1413     *            ActiveMQDestination
1414     * @throws Exception
1415     */
1416    public boolean moveMessageTo(ConnectionContext context, QueueMessageReference m, ActiveMQDestination dest) throws Exception {
1417        BrokerSupport.resend(context, m.getMessage(), dest);
1418        removeMessage(context, m);
1419        messagesLock.writeLock().lock();
1420        try {
1421            messages.rollback(m.getMessageId());
1422            if (isDLQ()) {
1423                DeadLetterStrategy stratagy = getDeadLetterStrategy();
1424                stratagy.rollback(m.getMessage());
1425            }
1426        } finally {
1427            messagesLock.writeLock().unlock();
1428        }
1429        return true;
1430    }
1431
1432    /**
1433     * Moves the message matching the given messageId
1434     */
1435    public boolean moveMessageTo(ConnectionContext context, String messageId, ActiveMQDestination dest)
1436            throws Exception {
1437        return moveMatchingMessagesTo(context, createMessageIdFilter(messageId), dest, 1) > 0;
1438    }
1439
1440    /**
1441     * Moves the messages matching the given selector
1442     *
1443     * @return the number of messages removed
1444     */
1445    public int moveMatchingMessagesTo(ConnectionContext context, String selector, ActiveMQDestination dest)
1446            throws Exception {
1447        return moveMatchingMessagesTo(context, selector, dest, Integer.MAX_VALUE);
1448    }
1449
1450    /**
1451     * Moves the messages matching the given selector up to the maximum number
1452     * of matched messages
1453     */
1454    public int moveMatchingMessagesTo(ConnectionContext context, String selector, ActiveMQDestination dest,
1455            int maximumMessages) throws Exception {
1456        return moveMatchingMessagesTo(context, createSelectorFilter(selector), dest, maximumMessages);
1457    }
1458
1459    /**
1460     * Moves the messages matching the given filter up to the maximum number of
1461     * matched messages
1462     */
1463    public int moveMatchingMessagesTo(ConnectionContext context, MessageReferenceFilter filter,
1464            ActiveMQDestination dest, int maximumMessages) throws Exception {
1465        int movedCounter = 0;
1466        Set<MessageReference> set = new LinkedHashSet<MessageReference>();
1467        do {
1468            doPageIn(true);
1469            pagedInMessagesLock.readLock().lock();
1470            try {
1471                set.addAll(pagedInMessages.values());
1472            } finally {
1473                pagedInMessagesLock.readLock().unlock();
1474            }
1475            List<MessageReference> list = new ArrayList<MessageReference>(set);
1476            for (MessageReference ref : list) {
1477                if (filter.evaluate(context, ref)) {
1478                    // We should only move messages that can be locked.
1479                    moveMessageTo(context, (QueueMessageReference)ref, dest);
1480                    set.remove(ref);
1481                    if (++movedCounter >= maximumMessages && maximumMessages > 0) {
1482                        return movedCounter;
1483                    }
1484                }
1485            }
1486        } while (set.size() < this.destinationStatistics.getMessages().getCount() && set.size() < maximumMessages);
1487        return movedCounter;
1488    }
1489
1490    public int retryMessages(ConnectionContext context, int maximumMessages) throws Exception {
1491        if (!isDLQ()) {
1492            throw new Exception("Retry of message is only possible on Dead Letter Queues!");
1493        }
1494        int restoredCounter = 0;
1495        Set<MessageReference> set = new LinkedHashSet<MessageReference>();
1496        do {
1497            doPageIn(true);
1498            pagedInMessagesLock.readLock().lock();
1499            try {
1500                set.addAll(pagedInMessages.values());
1501            } finally {
1502                pagedInMessagesLock.readLock().unlock();
1503            }
1504            List<MessageReference> list = new ArrayList<MessageReference>(set);
1505            for (MessageReference ref : list) {
1506                if (ref.getMessage().getOriginalDestination() != null) {
1507
1508                    moveMessageTo(context, (QueueMessageReference)ref, ref.getMessage().getOriginalDestination());
1509                    set.remove(ref);
1510                    if (++restoredCounter >= maximumMessages && maximumMessages > 0) {
1511                        return restoredCounter;
1512                    }
1513                }
1514            }
1515        } while (set.size() < this.destinationStatistics.getMessages().getCount() && set.size() < maximumMessages);
1516        return restoredCounter;
1517    }
1518
1519    /**
1520     * @return true if we would like to iterate again
1521     * @see org.apache.activemq.thread.Task#iterate()
1522     */
1523    @Override
1524    public boolean iterate() {
1525        MDC.put("activemq.destination", getName());
1526        boolean pageInMoreMessages = false;
1527        synchronized (iteratingMutex) {
1528
1529            // If optimize dispatch is on or this is a slave this method could be called recursively
1530            // we set this state value to short-circuit wakeup in those cases to avoid that as it
1531            // could lead to errors.
1532            iterationRunning = true;
1533
1534            // do early to allow dispatch of these waiting messages
1535            synchronized (messagesWaitingForSpace) {
1536                Iterator<Runnable> it = messagesWaitingForSpace.values().iterator();
1537                while (it.hasNext()) {
1538                    if (!memoryUsage.isFull()) {
1539                        Runnable op = it.next();
1540                        it.remove();
1541                        op.run();
1542                    } else {
1543                        registerCallbackForNotFullNotification();
1544                        break;
1545                    }
1546                }
1547            }
1548
1549            if (firstConsumer) {
1550                firstConsumer = false;
1551                try {
1552                    if (consumersBeforeDispatchStarts > 0) {
1553                        int timeout = 1000; // wait one second by default if
1554                                            // consumer count isn't reached
1555                        if (timeBeforeDispatchStarts > 0) {
1556                            timeout = timeBeforeDispatchStarts;
1557                        }
1558                        if (consumersBeforeStartsLatch.await(timeout, TimeUnit.MILLISECONDS)) {
1559                            LOG.debug("{} consumers subscribed. Starting dispatch.", consumers.size());
1560                        } else {
1561                            LOG.debug("{} ms elapsed and {} consumers subscribed. Starting dispatch.", timeout, consumers.size());
1562                        }
1563                    }
1564                    if (timeBeforeDispatchStarts > 0 && consumersBeforeDispatchStarts <= 0) {
1565                        iteratingMutex.wait(timeBeforeDispatchStarts);
1566                        LOG.debug("{} ms elapsed. Starting dispatch.", timeBeforeDispatchStarts);
1567                    }
1568                } catch (Exception e) {
1569                    LOG.error(e.toString());
1570                }
1571            }
1572
1573            messagesLock.readLock().lock();
1574            try{
1575                pageInMoreMessages |= !messages.isEmpty();
1576            } finally {
1577                messagesLock.readLock().unlock();
1578            }
1579
1580            pagedInPendingDispatchLock.readLock().lock();
1581            try {
1582                pageInMoreMessages |= !pagedInPendingDispatch.isEmpty();
1583            } finally {
1584                pagedInPendingDispatchLock.readLock().unlock();
1585            }
1586
1587            // Perhaps we should page always into the pagedInPendingDispatch
1588            // list if
1589            // !messages.isEmpty(), and then if
1590            // !pagedInPendingDispatch.isEmpty()
1591            // then we do a dispatch.
1592            boolean hasBrowsers = browserDispatches.size() > 0;
1593
1594            if (pageInMoreMessages || hasBrowsers || !redeliveredWaitingDispatch.isEmpty()) {
1595                try {
1596                    pageInMessages(hasBrowsers);
1597                } catch (Throwable e) {
1598                    LOG.error("Failed to page in more queue messages ", e);
1599                }
1600            }
1601
1602            if (hasBrowsers) {
1603                ArrayList<MessageReference> alreadyDispatchedMessages = null;
1604                pagedInMessagesLock.readLock().lock();
1605                try{
1606                    alreadyDispatchedMessages = new ArrayList<MessageReference>(pagedInMessages.values());
1607                }finally {
1608                    pagedInMessagesLock.readLock().unlock();
1609                }
1610
1611                Iterator<BrowserDispatch> browsers = browserDispatches.iterator();
1612                while (browsers.hasNext()) {
1613                    BrowserDispatch browserDispatch = browsers.next();
1614                    try {
1615                        MessageEvaluationContext msgContext = new NonCachedMessageEvaluationContext();
1616                        msgContext.setDestination(destination);
1617
1618                        QueueBrowserSubscription browser = browserDispatch.getBrowser();
1619
1620                        LOG.debug("dispatch to browser: {}, already dispatched/paged count: {}", browser, alreadyDispatchedMessages.size());
1621                        boolean added = false;
1622                        for (MessageReference node : alreadyDispatchedMessages) {
1623                            if (!((QueueMessageReference)node).isAcked() && !browser.isDuplicate(node.getMessageId()) && !browser.atMax()) {
1624                                msgContext.setMessageReference(node);
1625                                if (browser.matches(node, msgContext)) {
1626                                    browser.add(node);
1627                                    added = true;
1628                                }
1629                            }
1630                        }
1631                        // are we done browsing? no new messages paged
1632                        if (!added || browser.atMax()) {
1633                            browser.decrementQueueRef();
1634                            browserDispatches.remove(browserDispatch);
1635                        }
1636                    } catch (Exception e) {
1637                        LOG.warn("exception on dispatch to browser: {}", browserDispatch.getBrowser(), e);
1638                    }
1639                }
1640            }
1641
1642            if (pendingWakeups.get() > 0) {
1643                pendingWakeups.decrementAndGet();
1644            }
1645            MDC.remove("activemq.destination");
1646            iterationRunning = false;
1647
1648            return pendingWakeups.get() > 0;
1649        }
1650    }
1651
1652    protected MessageReferenceFilter createMessageIdFilter(final String messageId) {
1653        return new MessageReferenceFilter() {
1654            @Override
1655            public boolean evaluate(ConnectionContext context, MessageReference r) {
1656                return messageId.equals(r.getMessageId().toString());
1657            }
1658
1659            @Override
1660            public String toString() {
1661                return "MessageIdFilter: " + messageId;
1662            }
1663        };
1664    }
1665
1666    protected MessageReferenceFilter createSelectorFilter(String selector) throws InvalidSelectorException {
1667
1668        if (selector == null || selector.isEmpty()) {
1669            return new MessageReferenceFilter() {
1670
1671                @Override
1672                public boolean evaluate(ConnectionContext context, MessageReference messageReference) throws JMSException {
1673                    return true;
1674                }
1675            };
1676        }
1677
1678        final BooleanExpression selectorExpression = SelectorParser.parse(selector);
1679
1680        return new MessageReferenceFilter() {
1681            @Override
1682            public boolean evaluate(ConnectionContext context, MessageReference r) throws JMSException {
1683                MessageEvaluationContext messageEvaluationContext = context.getMessageEvaluationContext();
1684
1685                messageEvaluationContext.setMessageReference(r);
1686                if (messageEvaluationContext.getDestination() == null) {
1687                    messageEvaluationContext.setDestination(getActiveMQDestination());
1688                }
1689
1690                return selectorExpression.matches(messageEvaluationContext);
1691            }
1692        };
1693    }
1694
1695    protected void removeMessage(ConnectionContext c, QueueMessageReference r) throws IOException {
1696        removeMessage(c, null, r);
1697        pagedInPendingDispatchLock.writeLock().lock();
1698        try {
1699            pagedInPendingDispatch.remove(r);
1700        } finally {
1701            pagedInPendingDispatchLock.writeLock().unlock();
1702        }
1703    }
1704
1705    protected void removeMessage(ConnectionContext c, Subscription subs, QueueMessageReference r) throws IOException {
1706        MessageAck ack = new MessageAck();
1707        ack.setAckType(MessageAck.STANDARD_ACK_TYPE);
1708        ack.setDestination(destination);
1709        ack.setMessageID(r.getMessageId());
1710        removeMessage(c, subs, r, ack);
1711    }
1712
1713    protected void removeMessage(ConnectionContext context, Subscription sub, final QueueMessageReference reference,
1714            MessageAck ack) throws IOException {
1715        LOG.trace("ack of {} with {}", reference.getMessageId(), ack);
1716        // This sends the ack the the journal..
1717        if (!ack.isInTransaction()) {
1718            acknowledge(context, sub, ack, reference);
1719            getDestinationStatistics().getDequeues().increment();
1720            dropMessage(reference);
1721        } else {
1722            try {
1723                acknowledge(context, sub, ack, reference);
1724            } finally {
1725                context.getTransaction().addSynchronization(new Synchronization() {
1726
1727                    @Override
1728                    public void afterCommit() throws Exception {
1729                        getDestinationStatistics().getDequeues().increment();
1730                        dropMessage(reference);
1731                        wakeup();
1732                    }
1733
1734                    @Override
1735                    public void afterRollback() throws Exception {
1736                        reference.setAcked(false);
1737                        wakeup();
1738                    }
1739                });
1740            }
1741        }
1742        if (ack.isPoisonAck() || (sub != null && sub.getConsumerInfo().isNetworkSubscription())) {
1743            // message gone to DLQ, is ok to allow redelivery
1744            messagesLock.writeLock().lock();
1745            try {
1746                messages.rollback(reference.getMessageId());
1747            } finally {
1748                messagesLock.writeLock().unlock();
1749            }
1750            if (sub != null && sub.getConsumerInfo().isNetworkSubscription()) {
1751                getDestinationStatistics().getForwards().increment();
1752            }
1753        }
1754        // after successful store update
1755        reference.setAcked(true);
1756    }
1757
1758    private void dropMessage(QueueMessageReference reference) {
1759        if (!reference.isDropped()) {
1760            reference.drop();
1761            destinationStatistics.getMessages().decrement();
1762            pagedInMessagesLock.writeLock().lock();
1763            try {
1764                pagedInMessages.remove(reference);
1765            } finally {
1766                pagedInMessagesLock.writeLock().unlock();
1767            }
1768        }
1769    }
1770
1771    public void messageExpired(ConnectionContext context, MessageReference reference) {
1772        messageExpired(context, null, reference);
1773    }
1774
1775    @Override
1776    public void messageExpired(ConnectionContext context, Subscription subs, MessageReference reference) {
1777        LOG.debug("message expired: {}", reference);
1778        broker.messageExpired(context, reference, subs);
1779        destinationStatistics.getExpired().increment();
1780        try {
1781            removeMessage(context, subs, (QueueMessageReference) reference);
1782            messagesLock.writeLock().lock();
1783            try {
1784                messages.rollback(reference.getMessageId());
1785            } finally {
1786                messagesLock.writeLock().unlock();
1787            }
1788        } catch (IOException e) {
1789            LOG.error("Failed to remove expired Message from the store ", e);
1790        }
1791    }
1792
1793    final boolean cursorAdd(final Message msg) throws Exception {
1794        messagesLock.writeLock().lock();
1795        try {
1796            return messages.addMessageLast(msg);
1797        } finally {
1798            messagesLock.writeLock().unlock();
1799        }
1800    }
1801
1802    final void messageSent(final ConnectionContext context, final Message msg) throws Exception {
1803        destinationStatistics.getEnqueues().increment();
1804        destinationStatistics.getMessages().increment();
1805        destinationStatistics.getMessageSize().addSize(msg.getSize());
1806        messageDelivered(context, msg);
1807        consumersLock.readLock().lock();
1808        try {
1809            if (consumers.isEmpty()) {
1810                onMessageWithNoConsumers(context, msg);
1811            }
1812        }finally {
1813            consumersLock.readLock().unlock();
1814        }
1815        LOG.debug("{} Message {} sent to {}", new Object[]{ broker.getBrokerName(), msg.getMessageId(), this.destination });
1816        wakeup();
1817    }
1818
1819    @Override
1820    public void wakeup() {
1821        if (optimizedDispatch && !iterationRunning) {
1822            iterate();
1823            pendingWakeups.incrementAndGet();
1824        } else {
1825            asyncWakeup();
1826        }
1827    }
1828
1829    private void asyncWakeup() {
1830        try {
1831            pendingWakeups.incrementAndGet();
1832            this.taskRunner.wakeup();
1833        } catch (InterruptedException e) {
1834            LOG.warn("Async task runner failed to wakeup ", e);
1835        }
1836    }
1837
1838    private void doPageIn(boolean force) throws Exception {
1839        doPageIn(force, true);
1840    }
1841
1842    private void doPageIn(boolean force, boolean processExpired) throws Exception {
1843        PendingList newlyPaged = doPageInForDispatch(force, processExpired);
1844        pagedInPendingDispatchLock.writeLock().lock();
1845        try {
1846            if (pagedInPendingDispatch.isEmpty()) {
1847                pagedInPendingDispatch.addAll(newlyPaged);
1848
1849            } else {
1850                for (MessageReference qmr : newlyPaged) {
1851                    if (!pagedInPendingDispatch.contains(qmr)) {
1852                        pagedInPendingDispatch.addMessageLast(qmr);
1853                    }
1854                }
1855            }
1856        } finally {
1857            pagedInPendingDispatchLock.writeLock().unlock();
1858        }
1859    }
1860
1861    private PendingList doPageInForDispatch(boolean force, boolean processExpired) throws Exception {
1862        List<QueueMessageReference> result = null;
1863        PendingList resultList = null;
1864
1865        int toPageIn = Math.min(getMaxPageSize(), messages.size());
1866        int pagedInPendingSize = 0;
1867        pagedInPendingDispatchLock.readLock().lock();
1868        try {
1869            pagedInPendingSize = pagedInPendingDispatch.size();
1870        } finally {
1871            pagedInPendingDispatchLock.readLock().unlock();
1872        }
1873
1874        LOG.debug("{} toPageIn: {}, Inflight: {}, pagedInMessages.size {}, pagedInPendingDispatch.size {}, enqueueCount: {}, dequeueCount: {}, memUsage:{}",
1875                new Object[]{
1876                        this,
1877                        toPageIn,
1878                        destinationStatistics.getInflight().getCount(),
1879                        pagedInMessages.size(),
1880                        pagedInPendingSize,
1881                        destinationStatistics.getEnqueues().getCount(),
1882                        destinationStatistics.getDequeues().getCount(),
1883                        getMemoryUsage().getUsage()
1884                });
1885        if (isLazyDispatch() && !force) {
1886            // Only page in the minimum number of messages which can be
1887            // dispatched immediately.
1888            toPageIn = Math.min(getConsumerMessageCountBeforeFull(), toPageIn);
1889        }
1890        if (toPageIn > 0 && (force || (!consumers.isEmpty() && pagedInPendingSize < getMaxPageSize()))) {
1891            int count = 0;
1892            result = new ArrayList<QueueMessageReference>(toPageIn);
1893            messagesLock.writeLock().lock();
1894            try {
1895                try {
1896                    messages.setMaxBatchSize(toPageIn);
1897                    messages.reset();
1898                    while (messages.hasNext() && count < toPageIn) {
1899                        MessageReference node = messages.next();
1900                        messages.remove();
1901
1902                        QueueMessageReference ref = createMessageReference(node.getMessage());
1903                        if (processExpired && ref.isExpired()) {
1904                            if (broker.isExpired(ref)) {
1905                                messageExpired(createConnectionContext(), ref);
1906                            } else {
1907                                ref.decrementReferenceCount();
1908                            }
1909                        } else {
1910                            result.add(ref);
1911                            count++;
1912                        }
1913                    }
1914                } finally {
1915                    messages.release();
1916                }
1917            } finally {
1918                messagesLock.writeLock().unlock();
1919            }
1920            // Only add new messages, not already pagedIn to avoid multiple
1921            // dispatch attempts
1922            pagedInMessagesLock.writeLock().lock();
1923            try {
1924                if(isPrioritizedMessages()) {
1925                    resultList = new PrioritizedPendingList();
1926                } else {
1927                    resultList = new OrderedPendingList();
1928                }
1929                for (QueueMessageReference ref : result) {
1930                    if (!pagedInMessages.contains(ref)) {
1931                        pagedInMessages.addMessageLast(ref);
1932                        resultList.addMessageLast(ref);
1933                    } else {
1934                        ref.decrementReferenceCount();
1935                        // store should have trapped duplicate in it's index, also cursor audit
1936                        // we need to remove the duplicate from the store in the knowledge that the original message may be inflight
1937                        // note: jdbc store will not trap unacked messages as a duplicate b/c it gives each message a unique sequence id
1938                        LOG.warn("{}, duplicate message {} paged in, is cursor audit disabled? Removing from store and redirecting to dlq", this, ref.getMessage());
1939                        if (store != null) {
1940                            ConnectionContext connectionContext = createConnectionContext();
1941                            store.removeMessage(connectionContext, new MessageAck(ref.getMessage(), MessageAck.POSION_ACK_TYPE, 1));
1942                            broker.getRoot().sendToDeadLetterQueue(connectionContext, ref.getMessage(), null, new Throwable("duplicate paged in from store for " + destination));
1943                        }
1944                    }
1945                }
1946            } finally {
1947                pagedInMessagesLock.writeLock().unlock();
1948            }
1949        } else {
1950            // Avoid return null list, if condition is not validated
1951            resultList = new OrderedPendingList();
1952        }
1953
1954        return resultList;
1955    }
1956
1957    private void doDispatch(PendingList list) throws Exception {
1958        boolean doWakeUp = false;
1959
1960        pagedInPendingDispatchLock.writeLock().lock();
1961        try {
1962            if (!redeliveredWaitingDispatch.isEmpty()) {
1963                // Try first to dispatch redelivered messages to keep an
1964                // proper order
1965                redeliveredWaitingDispatch = doActualDispatch(redeliveredWaitingDispatch);
1966            }
1967            if (redeliveredWaitingDispatch.isEmpty()) {
1968                if (!pagedInPendingDispatch.isEmpty()) {
1969                    // Next dispatch anything that had not been
1970                    // dispatched before.
1971                    pagedInPendingDispatch = doActualDispatch(pagedInPendingDispatch);
1972                }
1973            }
1974            // and now see if we can dispatch the new stuff.. and append to the pending
1975            // list anything that does not actually get dispatched.
1976            if (list != null && !list.isEmpty()) {
1977                if (redeliveredWaitingDispatch.isEmpty() && pagedInPendingDispatch.isEmpty()) {
1978                    pagedInPendingDispatch.addAll(doActualDispatch(list));
1979                } else {
1980                    for (MessageReference qmr : list) {
1981                        if (!pagedInPendingDispatch.contains(qmr)) {
1982                            pagedInPendingDispatch.addMessageLast(qmr);
1983                        }
1984                    }
1985                    doWakeUp = true;
1986                }
1987            }
1988        } finally {
1989            pagedInPendingDispatchLock.writeLock().unlock();
1990        }
1991
1992        if (doWakeUp) {
1993            // avoid lock order contention
1994            asyncWakeup();
1995        }
1996    }
1997
1998    /**
1999     * @return list of messages that could get dispatched to consumers if they
2000     *         were not full.
2001     */
2002    private PendingList doActualDispatch(PendingList list) throws Exception {
2003        List<Subscription> consumers;
2004        consumersLock.readLock().lock();
2005
2006        try {
2007            if (this.consumers.isEmpty()) {
2008                // slave dispatch happens in processDispatchNotification
2009                return list;
2010            }
2011            consumers = new ArrayList<Subscription>(this.consumers);
2012        } finally {
2013            consumersLock.readLock().unlock();
2014        }
2015
2016        Set<Subscription> fullConsumers = new HashSet<Subscription>(this.consumers.size());
2017
2018        for (Iterator<MessageReference> iterator = list.iterator(); iterator.hasNext();) {
2019
2020            MessageReference node = iterator.next();
2021            Subscription target = null;
2022            for (Subscription s : consumers) {
2023                if (s instanceof QueueBrowserSubscription) {
2024                    continue;
2025                }
2026                if (!fullConsumers.contains(s)) {
2027                    if (!s.isFull()) {
2028                        if (dispatchSelector.canSelect(s, node) && assignMessageGroup(s, (QueueMessageReference)node) && !((QueueMessageReference) node).isAcked() ) {
2029                            // Dispatch it.
2030                            s.add(node);
2031                            LOG.trace("assigned {} to consumer {}", node.getMessageId(), s.getConsumerInfo().getConsumerId());
2032                            iterator.remove();
2033                            target = s;
2034                            break;
2035                        }
2036                    } else {
2037                        // no further dispatch of list to a full consumer to
2038                        // avoid out of order message receipt
2039                        fullConsumers.add(s);
2040                        LOG.trace("Subscription full {}", s);
2041                    }
2042                }
2043            }
2044
2045            if (target == null && node.isDropped()) {
2046                iterator.remove();
2047            }
2048
2049            // return if there are no consumers or all consumers are full
2050            if (target == null && consumers.size() == fullConsumers.size()) {
2051                return list;
2052            }
2053
2054            // If it got dispatched, rotate the consumer list to get round robin
2055            // distribution.
2056            if (target != null && !strictOrderDispatch && consumers.size() > 1
2057                    && !dispatchSelector.isExclusiveConsumer(target)) {
2058                consumersLock.writeLock().lock();
2059                try {
2060                    if (removeFromConsumerList(target)) {
2061                        addToConsumerList(target);
2062                        consumers = new ArrayList<Subscription>(this.consumers);
2063                    }
2064                } finally {
2065                    consumersLock.writeLock().unlock();
2066                }
2067            }
2068        }
2069
2070        return list;
2071    }
2072
2073    protected boolean assignMessageGroup(Subscription subscription, QueueMessageReference node) throws Exception {
2074        boolean result = true;
2075        // Keep message groups together.
2076        String groupId = node.getGroupID();
2077        int sequence = node.getGroupSequence();
2078        if (groupId != null) {
2079
2080            MessageGroupMap messageGroupOwners = getMessageGroupOwners();
2081            // If we can own the first, then no-one else should own the
2082            // rest.
2083            if (sequence == 1) {
2084                assignGroup(subscription, messageGroupOwners, node, groupId);
2085            } else {
2086
2087                // Make sure that the previous owner is still valid, we may
2088                // need to become the new owner.
2089                ConsumerId groupOwner;
2090
2091                groupOwner = messageGroupOwners.get(groupId);
2092                if (groupOwner == null) {
2093                    assignGroup(subscription, messageGroupOwners, node, groupId);
2094                } else {
2095                    if (groupOwner.equals(subscription.getConsumerInfo().getConsumerId())) {
2096                        // A group sequence < 1 is an end of group signal.
2097                        if (sequence < 0) {
2098                            messageGroupOwners.removeGroup(groupId);
2099                            subscription.getConsumerInfo().decrementAssignedGroupCount();
2100                        }
2101                    } else {
2102                        result = false;
2103                    }
2104                }
2105            }
2106        }
2107
2108        return result;
2109    }
2110
2111    protected void assignGroup(Subscription subs, MessageGroupMap messageGroupOwners, MessageReference n, String groupId) throws IOException {
2112        messageGroupOwners.put(groupId, subs.getConsumerInfo().getConsumerId());
2113        Message message = n.getMessage();
2114        message.setJMSXGroupFirstForConsumer(true);
2115        subs.getConsumerInfo().incrementAssignedGroupCount();
2116    }
2117
2118    protected void pageInMessages(boolean force) throws Exception {
2119        doDispatch(doPageInForDispatch(force, true));
2120    }
2121
2122    private void addToConsumerList(Subscription sub) {
2123        if (useConsumerPriority) {
2124            consumers.add(sub);
2125            Collections.sort(consumers, orderedCompare);
2126        } else {
2127            consumers.add(sub);
2128        }
2129    }
2130
2131    private boolean removeFromConsumerList(Subscription sub) {
2132        return consumers.remove(sub);
2133    }
2134
2135    private int getConsumerMessageCountBeforeFull() throws Exception {
2136        int total = 0;
2137        boolean zeroPrefetch = false;
2138        consumersLock.readLock().lock();
2139        try {
2140            for (Subscription s : consumers) {
2141                zeroPrefetch |= s.getPrefetchSize() == 0;
2142                int countBeforeFull = s.countBeforeFull();
2143                total += countBeforeFull;
2144            }
2145        } finally {
2146            consumersLock.readLock().unlock();
2147        }
2148        if (total == 0 && zeroPrefetch) {
2149            total = 1;
2150        }
2151        return total;
2152    }
2153
2154    /*
2155     * In slave mode, dispatch is ignored till we get this notification as the
2156     * dispatch process is non deterministic between master and slave. On a
2157     * notification, the actual dispatch to the subscription (as chosen by the
2158     * master) is completed. (non-Javadoc)
2159     * @see
2160     * org.apache.activemq.broker.region.BaseDestination#processDispatchNotification
2161     * (org.apache.activemq.command.MessageDispatchNotification)
2162     */
2163    @Override
2164    public void processDispatchNotification(MessageDispatchNotification messageDispatchNotification) throws Exception {
2165        // do dispatch
2166        Subscription sub = getMatchingSubscription(messageDispatchNotification);
2167        if (sub != null) {
2168            MessageReference message = getMatchingMessage(messageDispatchNotification);
2169            sub.add(message);
2170            sub.processMessageDispatchNotification(messageDispatchNotification);
2171        }
2172    }
2173
2174    private QueueMessageReference getMatchingMessage(MessageDispatchNotification messageDispatchNotification)
2175            throws Exception {
2176        QueueMessageReference message = null;
2177        MessageId messageId = messageDispatchNotification.getMessageId();
2178
2179        pagedInPendingDispatchLock.writeLock().lock();
2180        try {
2181            for (MessageReference ref : pagedInPendingDispatch) {
2182                if (messageId.equals(ref.getMessageId())) {
2183                    message = (QueueMessageReference)ref;
2184                    pagedInPendingDispatch.remove(ref);
2185                    break;
2186                }
2187            }
2188        } finally {
2189            pagedInPendingDispatchLock.writeLock().unlock();
2190        }
2191
2192        if (message == null) {
2193            pagedInMessagesLock.readLock().lock();
2194            try {
2195                message = (QueueMessageReference)pagedInMessages.get(messageId);
2196            } finally {
2197                pagedInMessagesLock.readLock().unlock();
2198            }
2199        }
2200
2201        if (message == null) {
2202            messagesLock.writeLock().lock();
2203            try {
2204                try {
2205                    messages.setMaxBatchSize(getMaxPageSize());
2206                    messages.reset();
2207                    while (messages.hasNext()) {
2208                        MessageReference node = messages.next();
2209                        messages.remove();
2210                        if (messageId.equals(node.getMessageId())) {
2211                            message = this.createMessageReference(node.getMessage());
2212                            break;
2213                        }
2214                    }
2215                } finally {
2216                    messages.release();
2217                }
2218            } finally {
2219                messagesLock.writeLock().unlock();
2220            }
2221        }
2222
2223        if (message == null) {
2224            Message msg = loadMessage(messageId);
2225            if (msg != null) {
2226                message = this.createMessageReference(msg);
2227            }
2228        }
2229
2230        if (message == null) {
2231            throw new JMSException("Slave broker out of sync with master - Message: "
2232                    + messageDispatchNotification.getMessageId() + " on "
2233                    + messageDispatchNotification.getDestination() + " does not exist among pending("
2234                    + pagedInPendingDispatch.size() + ") for subscription: "
2235                    + messageDispatchNotification.getConsumerId());
2236        }
2237        return message;
2238    }
2239
2240    /**
2241     * Find a consumer that matches the id in the message dispatch notification
2242     *
2243     * @param messageDispatchNotification
2244     * @return sub or null if the subscription has been removed before dispatch
2245     * @throws JMSException
2246     */
2247    private Subscription getMatchingSubscription(MessageDispatchNotification messageDispatchNotification)
2248            throws JMSException {
2249        Subscription sub = null;
2250        consumersLock.readLock().lock();
2251        try {
2252            for (Subscription s : consumers) {
2253                if (messageDispatchNotification.getConsumerId().equals(s.getConsumerInfo().getConsumerId())) {
2254                    sub = s;
2255                    break;
2256                }
2257            }
2258        } finally {
2259            consumersLock.readLock().unlock();
2260        }
2261        return sub;
2262    }
2263
2264    @Override
2265    public void onUsageChanged(@SuppressWarnings("rawtypes") Usage usage, int oldPercentUsage, int newPercentUsage) {
2266        if (oldPercentUsage > newPercentUsage) {
2267            asyncWakeup();
2268        }
2269    }
2270
2271    @Override
2272    protected Logger getLog() {
2273        return LOG;
2274    }
2275
2276    protected boolean isOptimizeStorage(){
2277        boolean result = false;
2278        if (isDoOptimzeMessageStorage()){
2279            consumersLock.readLock().lock();
2280            try{
2281                if (consumers.isEmpty()==false){
2282                    result = true;
2283                    for (Subscription s : consumers) {
2284                        if (s.getPrefetchSize()==0){
2285                            result = false;
2286                            break;
2287                        }
2288                        if (s.isSlowConsumer()){
2289                            result = false;
2290                            break;
2291                        }
2292                        if (s.getInFlightUsage() > getOptimizeMessageStoreInFlightLimit()){
2293                            result = false;
2294                            break;
2295                        }
2296                    }
2297                }
2298            } finally {
2299                consumersLock.readLock().unlock();
2300            }
2301        }
2302        return result;
2303    }
2304}