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