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