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