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