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