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