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