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