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