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