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 java.io.IOException; 020import java.net.URI; 021import java.util.ArrayList; 022import java.util.Collections; 023import java.util.HashMap; 024import java.util.List; 025import java.util.Locale; 026import java.util.Map; 027import java.util.Set; 028import java.util.concurrent.ConcurrentHashMap; 029import java.util.concurrent.CopyOnWriteArrayList; 030import java.util.concurrent.ThreadPoolExecutor; 031import java.util.concurrent.atomic.AtomicBoolean; 032import java.util.concurrent.locks.ReentrantReadWriteLock; 033 034import javax.jms.InvalidClientIDException; 035import javax.jms.JMSException; 036 037import org.apache.activemq.broker.Broker; 038import org.apache.activemq.broker.BrokerService; 039import org.apache.activemq.broker.Connection; 040import org.apache.activemq.broker.ConnectionContext; 041import org.apache.activemq.broker.ConsumerBrokerExchange; 042import org.apache.activemq.broker.EmptyBroker; 043import org.apache.activemq.broker.ProducerBrokerExchange; 044import org.apache.activemq.broker.TransportConnection; 045import org.apache.activemq.broker.TransportConnector; 046import org.apache.activemq.broker.region.policy.DeadLetterStrategy; 047import org.apache.activemq.broker.region.policy.PolicyMap; 048import org.apache.activemq.command.ActiveMQDestination; 049import org.apache.activemq.command.ActiveMQMessage; 050import org.apache.activemq.command.BrokerId; 051import org.apache.activemq.command.BrokerInfo; 052import org.apache.activemq.command.ConnectionId; 053import org.apache.activemq.command.ConnectionInfo; 054import org.apache.activemq.command.ConsumerControl; 055import org.apache.activemq.command.ConsumerInfo; 056import org.apache.activemq.command.DestinationInfo; 057import org.apache.activemq.command.Message; 058import org.apache.activemq.command.MessageAck; 059import org.apache.activemq.command.MessageDispatch; 060import org.apache.activemq.command.MessageDispatchNotification; 061import org.apache.activemq.command.MessagePull; 062import org.apache.activemq.command.ProducerInfo; 063import org.apache.activemq.command.RemoveSubscriptionInfo; 064import org.apache.activemq.command.Response; 065import org.apache.activemq.command.TransactionId; 066import org.apache.activemq.state.ConnectionState; 067import org.apache.activemq.store.PListStore; 068import org.apache.activemq.thread.Scheduler; 069import org.apache.activemq.thread.TaskRunnerFactory; 070import org.apache.activemq.transport.TransmitCallback; 071import org.apache.activemq.usage.SystemUsage; 072import org.apache.activemq.util.BrokerSupport; 073import org.apache.activemq.util.IdGenerator; 074import org.apache.activemq.util.InetAddressUtil; 075import org.apache.activemq.util.LongSequenceGenerator; 076import org.apache.activemq.util.ServiceStopper; 077import org.slf4j.Logger; 078import org.slf4j.LoggerFactory; 079 080/** 081 * Routes Broker operations to the correct messaging regions for processing. 082 */ 083public class RegionBroker extends EmptyBroker { 084 public static final String ORIGINAL_EXPIRATION = "originalExpiration"; 085 private static final Logger LOG = LoggerFactory.getLogger(RegionBroker.class); 086 private static final IdGenerator BROKER_ID_GENERATOR = new IdGenerator(); 087 088 protected final DestinationStatistics destinationStatistics = new DestinationStatistics(); 089 protected DestinationFactory destinationFactory; 090 protected final Map<ConnectionId, ConnectionState> connectionStates = Collections.synchronizedMap(new HashMap<ConnectionId, ConnectionState>()); 091 092 private final Region queueRegion; 093 private final Region topicRegion; 094 private final Region tempQueueRegion; 095 private final Region tempTopicRegion; 096 protected final BrokerService brokerService; 097 private boolean started; 098 private boolean keepDurableSubsActive; 099 100 private final CopyOnWriteArrayList<Connection> connections = new CopyOnWriteArrayList<Connection>(); 101 private final Map<ActiveMQDestination, ActiveMQDestination> destinationGate = new HashMap<ActiveMQDestination, ActiveMQDestination>(); 102 private final Map<ActiveMQDestination, Destination> destinations = new ConcurrentHashMap<ActiveMQDestination, Destination>(); 103 private final Map<BrokerId, BrokerInfo> brokerInfos = new HashMap<BrokerId, BrokerInfo>(); 104 105 private final LongSequenceGenerator sequenceGenerator = new LongSequenceGenerator(); 106 private BrokerId brokerId; 107 private String brokerName; 108 private final Map<String, ConnectionContext> clientIdSet = new HashMap<String, ConnectionContext>(); 109 private final DestinationInterceptor destinationInterceptor; 110 private ConnectionContext adminConnectionContext; 111 private final Scheduler scheduler; 112 private final ThreadPoolExecutor executor; 113 private boolean allowTempAutoCreationOnSend; 114 115 private final ReentrantReadWriteLock inactiveDestinationsPurgeLock = new ReentrantReadWriteLock(); 116 private final TaskRunnerFactory taskRunnerFactory; 117 private final AtomicBoolean purgeInactiveDestinationsTaskInProgress = new AtomicBoolean(false); 118 private final Runnable purgeInactiveDestinationsTask = new Runnable() { 119 @Override 120 public void run() { 121 if (purgeInactiveDestinationsTaskInProgress.compareAndSet(false, true)) { 122 taskRunnerFactory.execute(purgeInactiveDestinationsWork); 123 } 124 } 125 }; 126 private final Runnable purgeInactiveDestinationsWork = new Runnable() { 127 @Override 128 public void run() { 129 try { 130 purgeInactiveDestinations(); 131 } catch (Throwable ignored) { 132 LOG.error("Unexpected exception on purgeInactiveDestinations {}", this, ignored); 133 } finally { 134 purgeInactiveDestinationsTaskInProgress.set(false); 135 } 136 } 137 }; 138 139 public RegionBroker(BrokerService brokerService, TaskRunnerFactory taskRunnerFactory, SystemUsage memoryManager, DestinationFactory destinationFactory, 140 DestinationInterceptor destinationInterceptor, Scheduler scheduler, ThreadPoolExecutor executor) throws IOException { 141 this.brokerService = brokerService; 142 this.executor = executor; 143 this.scheduler = scheduler; 144 if (destinationFactory == null) { 145 throw new IllegalArgumentException("null destinationFactory"); 146 } 147 this.sequenceGenerator.setLastSequenceId(destinationFactory.getLastMessageBrokerSequenceId()); 148 this.destinationFactory = destinationFactory; 149 queueRegion = createQueueRegion(memoryManager, taskRunnerFactory, destinationFactory); 150 topicRegion = createTopicRegion(memoryManager, taskRunnerFactory, destinationFactory); 151 this.destinationInterceptor = destinationInterceptor; 152 tempQueueRegion = createTempQueueRegion(memoryManager, taskRunnerFactory, destinationFactory); 153 tempTopicRegion = createTempTopicRegion(memoryManager, taskRunnerFactory, destinationFactory); 154 this.taskRunnerFactory = taskRunnerFactory; 155 } 156 157 @Override 158 public Map<ActiveMQDestination, Destination> getDestinationMap() { 159 Map<ActiveMQDestination, Destination> answer = new HashMap<ActiveMQDestination, Destination>(getQueueRegion().getDestinationMap()); 160 answer.putAll(getTopicRegion().getDestinationMap()); 161 return answer; 162 } 163 164 @Override 165 public Map<ActiveMQDestination, Destination> getDestinationMap(ActiveMQDestination destination) { 166 try { 167 return getRegion(destination).getDestinationMap(); 168 } catch (JMSException jmse) { 169 return Collections.emptyMap(); 170 } 171 } 172 173 @Override 174 public Set<Destination> getDestinations(ActiveMQDestination destination) { 175 try { 176 return getRegion(destination).getDestinations(destination); 177 } catch (JMSException jmse) { 178 return Collections.emptySet(); 179 } 180 } 181 182 public Region getQueueRegion() { 183 return queueRegion; 184 } 185 186 public Region getTempQueueRegion() { 187 return tempQueueRegion; 188 } 189 190 public Region getTempTopicRegion() { 191 return tempTopicRegion; 192 } 193 194 public Region getTopicRegion() { 195 return topicRegion; 196 } 197 198 protected Region createTempTopicRegion(SystemUsage memoryManager, TaskRunnerFactory taskRunnerFactory, DestinationFactory destinationFactory) { 199 return new TempTopicRegion(this, destinationStatistics, memoryManager, taskRunnerFactory, destinationFactory); 200 } 201 202 protected Region createTempQueueRegion(SystemUsage memoryManager, TaskRunnerFactory taskRunnerFactory, DestinationFactory destinationFactory) { 203 return new TempQueueRegion(this, destinationStatistics, memoryManager, taskRunnerFactory, destinationFactory); 204 } 205 206 protected Region createTopicRegion(SystemUsage memoryManager, TaskRunnerFactory taskRunnerFactory, DestinationFactory destinationFactory) { 207 return new TopicRegion(this, destinationStatistics, memoryManager, taskRunnerFactory, destinationFactory); 208 } 209 210 protected Region createQueueRegion(SystemUsage memoryManager, TaskRunnerFactory taskRunnerFactory, DestinationFactory destinationFactory) { 211 return new QueueRegion(this, destinationStatistics, memoryManager, taskRunnerFactory, destinationFactory); 212 } 213 214 @Override 215 public void start() throws Exception { 216 started = true; 217 queueRegion.start(); 218 topicRegion.start(); 219 tempQueueRegion.start(); 220 tempTopicRegion.start(); 221 int period = this.brokerService.getSchedulePeriodForDestinationPurge(); 222 if (period > 0) { 223 this.scheduler.executePeriodically(purgeInactiveDestinationsTask, period); 224 } 225 } 226 227 @Override 228 public void stop() throws Exception { 229 started = false; 230 this.scheduler.cancel(purgeInactiveDestinationsTask); 231 ServiceStopper ss = new ServiceStopper(); 232 doStop(ss); 233 ss.throwFirstException(); 234 // clear the state 235 clientIdSet.clear(); 236 connections.clear(); 237 destinations.clear(); 238 brokerInfos.clear(); 239 } 240 241 public PolicyMap getDestinationPolicy() { 242 return brokerService != null ? brokerService.getDestinationPolicy() : null; 243 } 244 245 public ConnectionContext getConnectionContext(String clientId) { 246 return clientIdSet.get(clientId); 247 } 248 249 @Override 250 public void addConnection(ConnectionContext context, ConnectionInfo info) throws Exception { 251 String clientId = info.getClientId(); 252 if (clientId == null) { 253 throw new InvalidClientIDException("No clientID specified for connection request"); 254 } 255 256 ConnectionContext oldContext = null; 257 258 synchronized (clientIdSet) { 259 oldContext = clientIdSet.get(clientId); 260 if (oldContext != null) { 261 if (context.isAllowLinkStealing()) { 262 clientIdSet.put(clientId, context); 263 } else { 264 throw new InvalidClientIDException("Broker: " + getBrokerName() + " - Client: " + clientId + " already connected from " 265 + oldContext.getConnection().getRemoteAddress()); 266 } 267 } else { 268 clientIdSet.put(clientId, context); 269 } 270 } 271 272 if (oldContext != null) { 273 if (oldContext.getConnection() != null) { 274 Connection connection = oldContext.getConnection(); 275 LOG.warn("Stealing link for clientId {} From Connection {}", clientId, oldContext.getConnection()); 276 if (connection instanceof TransportConnection) { 277 TransportConnection transportConnection = (TransportConnection) connection; 278 transportConnection.stopAsync(new IOException("Stealing link for clientId " + clientId + " From Connection " + oldContext.getConnection().getConnectionId())); 279 } else { 280 connection.stop(); 281 } 282 } else { 283 LOG.error("No Connection found for {}", oldContext); 284 } 285 } 286 287 connections.add(context.getConnection()); 288 } 289 290 @Override 291 public void removeConnection(ConnectionContext context, ConnectionInfo info, Throwable error) throws Exception { 292 String clientId = info.getClientId(); 293 if (clientId == null) { 294 throw new InvalidClientIDException("No clientID specified for connection disconnect request"); 295 } 296 synchronized (clientIdSet) { 297 ConnectionContext oldValue = clientIdSet.get(clientId); 298 // we may be removing the duplicate connection, not the first connection to be created 299 // so lets check that their connection IDs are the same 300 if (oldValue == context) { 301 if (isEqual(oldValue.getConnectionId(), info.getConnectionId())) { 302 clientIdSet.remove(clientId); 303 } 304 } 305 } 306 connections.remove(context.getConnection()); 307 } 308 309 protected boolean isEqual(ConnectionId connectionId, ConnectionId connectionId2) { 310 return connectionId == connectionId2 || (connectionId != null && connectionId.equals(connectionId2)); 311 } 312 313 @Override 314 public Connection[] getClients() throws Exception { 315 ArrayList<Connection> l = new ArrayList<Connection>(connections); 316 Connection rc[] = new Connection[l.size()]; 317 l.toArray(rc); 318 return rc; 319 } 320 321 @Override 322 public Destination addDestination(ConnectionContext context, ActiveMQDestination destination, boolean createIfTemp) throws Exception { 323 324 Destination answer; 325 326 answer = destinations.get(destination); 327 if (answer != null) { 328 return answer; 329 } 330 331 synchronized (destinationGate) { 332 answer = destinations.get(destination); 333 if (answer != null) { 334 return answer; 335 } 336 337 if (destinationGate.get(destination) != null) { 338 // Guard against spurious wakeup. 339 while (destinationGate.containsKey(destination)) { 340 destinationGate.wait(); 341 } 342 answer = destinations.get(destination); 343 if (answer != null) { 344 return answer; 345 } else { 346 // In case of intermediate remove or add failure 347 destinationGate.put(destination, destination); 348 } 349 } 350 } 351 352 try { 353 boolean create = true; 354 if (destination.isTemporary()) { 355 create = createIfTemp; 356 } 357 answer = getRegion(destination).addDestination(context, destination, create); 358 destinations.put(destination, answer); 359 } finally { 360 synchronized (destinationGate) { 361 destinationGate.remove(destination); 362 destinationGate.notifyAll(); 363 } 364 } 365 366 return answer; 367 } 368 369 @Override 370 public void removeDestination(ConnectionContext context, ActiveMQDestination destination, long timeout) throws Exception { 371 if (destinations.containsKey(destination)) { 372 getRegion(destination).removeDestination(context, destination, timeout); 373 destinations.remove(destination); 374 } 375 } 376 377 @Override 378 public void addDestinationInfo(ConnectionContext context, DestinationInfo info) throws Exception { 379 addDestination(context, info.getDestination(), true); 380 381 } 382 383 @Override 384 public void removeDestinationInfo(ConnectionContext context, DestinationInfo info) throws Exception { 385 removeDestination(context, info.getDestination(), info.getTimeout()); 386 } 387 388 @Override 389 public ActiveMQDestination[] getDestinations() throws Exception { 390 ArrayList<ActiveMQDestination> l; 391 392 l = new ArrayList<ActiveMQDestination>(getDestinationMap().keySet()); 393 394 ActiveMQDestination rc[] = new ActiveMQDestination[l.size()]; 395 l.toArray(rc); 396 return rc; 397 } 398 399 @Override 400 public void addProducer(ConnectionContext context, ProducerInfo info) throws Exception { 401 ActiveMQDestination destination = info.getDestination(); 402 if (destination != null) { 403 inactiveDestinationsPurgeLock.readLock().lock(); 404 try { 405 // This seems to cause the destination to be added but without 406 // advisories firing... 407 context.getBroker().addDestination(context, destination, isAllowTempAutoCreationOnSend()); 408 getRegion(destination).addProducer(context, info); 409 } finally { 410 inactiveDestinationsPurgeLock.readLock().unlock(); 411 } 412 } 413 } 414 415 @Override 416 public void removeProducer(ConnectionContext context, ProducerInfo info) throws Exception { 417 ActiveMQDestination destination = info.getDestination(); 418 if (destination != null) { 419 inactiveDestinationsPurgeLock.readLock().lock(); 420 try { 421 getRegion(destination).removeProducer(context, info); 422 } finally { 423 inactiveDestinationsPurgeLock.readLock().unlock(); 424 } 425 } 426 } 427 428 @Override 429 public Subscription addConsumer(ConnectionContext context, ConsumerInfo info) throws Exception { 430 ActiveMQDestination destination = info.getDestination(); 431 if (destinationInterceptor != null) { 432 destinationInterceptor.create(this, context, destination); 433 } 434 inactiveDestinationsPurgeLock.readLock().lock(); 435 try { 436 return getRegion(destination).addConsumer(context, info); 437 } finally { 438 inactiveDestinationsPurgeLock.readLock().unlock(); 439 } 440 } 441 442 @Override 443 public void removeConsumer(ConnectionContext context, ConsumerInfo info) throws Exception { 444 ActiveMQDestination destination = info.getDestination(); 445 inactiveDestinationsPurgeLock.readLock().lock(); 446 try { 447 getRegion(destination).removeConsumer(context, info); 448 } finally { 449 inactiveDestinationsPurgeLock.readLock().unlock(); 450 } 451 } 452 453 @Override 454 public void removeSubscription(ConnectionContext context, RemoveSubscriptionInfo info) throws Exception { 455 inactiveDestinationsPurgeLock.readLock().lock(); 456 try { 457 topicRegion.removeSubscription(context, info); 458 } finally { 459 inactiveDestinationsPurgeLock.readLock().unlock(); 460 461 } 462 } 463 464 @Override 465 public void send(ProducerBrokerExchange producerExchange, Message message) throws Exception { 466 ActiveMQDestination destination = message.getDestination(); 467 message.setBrokerInTime(System.currentTimeMillis()); 468 if (producerExchange.isMutable() || producerExchange.getRegion() == null 469 || (producerExchange.getRegionDestination() != null && producerExchange.getRegionDestination().isDisposed())) { 470 // ensure the destination is registered with the RegionBroker 471 producerExchange.getConnectionContext().getBroker() 472 .addDestination(producerExchange.getConnectionContext(), destination, isAllowTempAutoCreationOnSend()); 473 producerExchange.setRegion(getRegion(destination)); 474 producerExchange.setRegionDestination(null); 475 } 476 477 producerExchange.getRegion().send(producerExchange, message); 478 479 // clean up so these references aren't kept (possible leak) in the producer exchange 480 // especially since temps are transitory 481 if (producerExchange.isMutable()) { 482 producerExchange.setRegionDestination(null); 483 producerExchange.setRegion(null); 484 } 485 } 486 487 @Override 488 public void acknowledge(ConsumerBrokerExchange consumerExchange, MessageAck ack) throws Exception { 489 if (consumerExchange.isWildcard() || consumerExchange.getRegion() == null) { 490 ActiveMQDestination destination = ack.getDestination(); 491 consumerExchange.setRegion(getRegion(destination)); 492 } 493 consumerExchange.getRegion().acknowledge(consumerExchange, ack); 494 } 495 496 public Region getRegion(ActiveMQDestination destination) throws JMSException { 497 switch (destination.getDestinationType()) { 498 case ActiveMQDestination.QUEUE_TYPE: 499 return queueRegion; 500 case ActiveMQDestination.TOPIC_TYPE: 501 return topicRegion; 502 case ActiveMQDestination.TEMP_QUEUE_TYPE: 503 return tempQueueRegion; 504 case ActiveMQDestination.TEMP_TOPIC_TYPE: 505 return tempTopicRegion; 506 default: 507 throw createUnknownDestinationTypeException(destination); 508 } 509 } 510 511 @Override 512 public Response messagePull(ConnectionContext context, MessagePull pull) throws Exception { 513 ActiveMQDestination destination = pull.getDestination(); 514 return getRegion(destination).messagePull(context, pull); 515 } 516 517 @Override 518 public TransactionId[] getPreparedTransactions(ConnectionContext context) throws Exception { 519 throw new IllegalAccessException("Transaction operation not implemented by this broker."); 520 } 521 522 @Override 523 public void beginTransaction(ConnectionContext context, TransactionId xid) throws Exception { 524 throw new IllegalAccessException("Transaction operation not implemented by this broker."); 525 } 526 527 @Override 528 public int prepareTransaction(ConnectionContext context, TransactionId xid) throws Exception { 529 throw new IllegalAccessException("Transaction operation not implemented by this broker."); 530 } 531 532 @Override 533 public void rollbackTransaction(ConnectionContext context, TransactionId xid) throws Exception { 534 throw new IllegalAccessException("Transaction operation not implemented by this broker."); 535 } 536 537 @Override 538 public void commitTransaction(ConnectionContext context, TransactionId xid, boolean onePhase) throws Exception { 539 throw new IllegalAccessException("Transaction operation not implemented by this broker."); 540 } 541 542 @Override 543 public void forgetTransaction(ConnectionContext context, TransactionId transactionId) throws Exception { 544 throw new IllegalAccessException("Transaction operation not implemented by this broker."); 545 } 546 547 @Override 548 public void gc() { 549 queueRegion.gc(); 550 topicRegion.gc(); 551 } 552 553 @Override 554 public BrokerId getBrokerId() { 555 if (brokerId == null) { 556 brokerId = new BrokerId(BROKER_ID_GENERATOR.generateId()); 557 } 558 return brokerId; 559 } 560 561 public void setBrokerId(BrokerId brokerId) { 562 this.brokerId = brokerId; 563 } 564 565 @Override 566 public String getBrokerName() { 567 if (brokerName == null) { 568 try { 569 brokerName = InetAddressUtil.getLocalHostName().toLowerCase(Locale.ENGLISH); 570 } catch (Exception e) { 571 brokerName = "localhost"; 572 } 573 } 574 return brokerName; 575 } 576 577 public void setBrokerName(String brokerName) { 578 this.brokerName = brokerName; 579 } 580 581 public DestinationStatistics getDestinationStatistics() { 582 return destinationStatistics; 583 } 584 585 protected JMSException createUnknownDestinationTypeException(ActiveMQDestination destination) { 586 return new JMSException("Unknown destination type: " + destination.getDestinationType()); 587 } 588 589 @Override 590 public synchronized void addBroker(Connection connection, BrokerInfo info) { 591 BrokerInfo existing = brokerInfos.get(info.getBrokerId()); 592 if (existing == null) { 593 existing = info.copy(); 594 existing.setPeerBrokerInfos(null); 595 brokerInfos.put(info.getBrokerId(), existing); 596 } 597 existing.incrementRefCount(); 598 LOG.debug("{} addBroker: {} brokerInfo size: {}", new Object[]{ getBrokerName(), info.getBrokerName(), brokerInfos.size() }); 599 addBrokerInClusterUpdate(info); 600 } 601 602 @Override 603 public synchronized void removeBroker(Connection connection, BrokerInfo info) { 604 if (info != null) { 605 BrokerInfo existing = brokerInfos.get(info.getBrokerId()); 606 if (existing != null && existing.decrementRefCount() == 0) { 607 brokerInfos.remove(info.getBrokerId()); 608 } 609 LOG.debug("{} removeBroker: {} brokerInfo size: {}", new Object[]{ getBrokerName(), info.getBrokerName(), brokerInfos.size()}); 610 // When stopping don't send cluster updates since we are the one's tearing down 611 // our own bridges. 612 if (!brokerService.isStopping()) { 613 removeBrokerInClusterUpdate(info); 614 } 615 } 616 } 617 618 @Override 619 public synchronized BrokerInfo[] getPeerBrokerInfos() { 620 BrokerInfo[] result = new BrokerInfo[brokerInfos.size()]; 621 result = brokerInfos.values().toArray(result); 622 return result; 623 } 624 625 @Override 626 public void preProcessDispatch(final MessageDispatch messageDispatch) { 627 final Message message = messageDispatch.getMessage(); 628 if (message != null) { 629 long endTime = System.currentTimeMillis(); 630 message.setBrokerOutTime(endTime); 631 if (getBrokerService().isEnableStatistics()) { 632 long totalTime = endTime - message.getBrokerInTime(); 633 ((Destination) message.getRegionDestination()).getDestinationStatistics().getProcessTime().addTime(totalTime); 634 } 635 if (((BaseDestination) message.getRegionDestination()).isPersistJMSRedelivered() && !message.isRedelivered()) { 636 final int originalValue = message.getRedeliveryCounter(); 637 message.incrementRedeliveryCounter(); 638 try { 639 if (message.isPersistent()) { 640 ((BaseDestination) message.getRegionDestination()).getMessageStore().updateMessage(message); 641 } 642 messageDispatch.setTransmitCallback(new TransmitCallback() { 643 // dispatch is considered a delivery, so update sub state post dispatch otherwise 644 // on a disconnect/reconnect cached messages will not reflect initial delivery attempt 645 final TransmitCallback delegate = messageDispatch.getTransmitCallback(); 646 @Override 647 public void onSuccess() { 648 message.incrementRedeliveryCounter(); 649 if (delegate != null) { 650 delegate.onSuccess(); 651 } 652 } 653 654 @Override 655 public void onFailure() { 656 if (delegate != null) { 657 delegate.onFailure(); 658 } 659 } 660 }); 661 } catch (IOException error) { 662 RuntimeException runtimeException = new RuntimeException("Failed to persist JMSRedeliveryFlag on " + message.getMessageId() + " in " + message.getDestination(), error); 663 LOG.warn(runtimeException.getLocalizedMessage(), runtimeException); 664 throw runtimeException; 665 } finally { 666 message.setRedeliveryCounter(originalValue); 667 } 668 } 669 } 670 } 671 672 @Override 673 public void postProcessDispatch(MessageDispatch messageDispatch) { 674 } 675 676 @Override 677 public void processDispatchNotification(MessageDispatchNotification messageDispatchNotification) throws Exception { 678 ActiveMQDestination destination = messageDispatchNotification.getDestination(); 679 getRegion(destination).processDispatchNotification(messageDispatchNotification); 680 } 681 682 @Override 683 public boolean isStopped() { 684 return !started; 685 } 686 687 @Override 688 public Set<ActiveMQDestination> getDurableDestinations() { 689 return destinationFactory.getDestinations(); 690 } 691 692 protected void doStop(ServiceStopper ss) { 693 ss.stop(queueRegion); 694 ss.stop(topicRegion); 695 ss.stop(tempQueueRegion); 696 ss.stop(tempTopicRegion); 697 } 698 699 public boolean isKeepDurableSubsActive() { 700 return keepDurableSubsActive; 701 } 702 703 public void setKeepDurableSubsActive(boolean keepDurableSubsActive) { 704 this.keepDurableSubsActive = keepDurableSubsActive; 705 ((TopicRegion) topicRegion).setKeepDurableSubsActive(keepDurableSubsActive); 706 } 707 708 public DestinationInterceptor getDestinationInterceptor() { 709 return destinationInterceptor; 710 } 711 712 @Override 713 public ConnectionContext getAdminConnectionContext() { 714 return adminConnectionContext; 715 } 716 717 @Override 718 public void setAdminConnectionContext(ConnectionContext adminConnectionContext) { 719 this.adminConnectionContext = adminConnectionContext; 720 } 721 722 public Map<ConnectionId, ConnectionState> getConnectionStates() { 723 return connectionStates; 724 } 725 726 @Override 727 public PListStore getTempDataStore() { 728 return brokerService.getTempDataStore(); 729 } 730 731 @Override 732 public URI getVmConnectorURI() { 733 return brokerService.getVmConnectorURI(); 734 } 735 736 @Override 737 public void brokerServiceStarted() { 738 } 739 740 @Override 741 public BrokerService getBrokerService() { 742 return brokerService; 743 } 744 745 @Override 746 public boolean isExpired(MessageReference messageReference) { 747 return messageReference.canProcessAsExpired(); 748 } 749 750 private boolean stampAsExpired(Message message) throws IOException { 751 boolean stamped = false; 752 if (message.getProperty(ORIGINAL_EXPIRATION) == null) { 753 long expiration = message.getExpiration(); 754 message.setProperty(ORIGINAL_EXPIRATION, new Long(expiration)); 755 stamped = true; 756 } 757 return stamped; 758 } 759 760 @Override 761 public void messageExpired(ConnectionContext context, MessageReference node, Subscription subscription) { 762 LOG.debug("Message expired {}", node); 763 getRoot().sendToDeadLetterQueue(context, node, subscription, new Throwable("Message Expired. Expiration:" + node.getExpiration())); 764 } 765 766 @Override 767 public boolean sendToDeadLetterQueue(ConnectionContext context, MessageReference node, Subscription subscription, Throwable poisonCause) { 768 try { 769 if (node != null) { 770 Message message = node.getMessage(); 771 if (message != null && node.getRegionDestination() != null) { 772 DeadLetterStrategy deadLetterStrategy = ((Destination) node.getRegionDestination()).getDeadLetterStrategy(); 773 if (deadLetterStrategy != null) { 774 if (deadLetterStrategy.isSendToDeadLetterQueue(message)) { 775 ActiveMQDestination deadLetterDestination = deadLetterStrategy.getDeadLetterQueueFor(message, subscription); 776 // Prevent a DLQ loop where same message is sent from a DLQ back to itself 777 if (deadLetterDestination.equals(message.getDestination())) { 778 LOG.debug("Not re-adding to DLQ: {}, dest: {}", message.getMessageId(), message.getDestination()); 779 return false; 780 } 781 782 // message may be inflight to other subscriptions so do not modify 783 message = message.copy(); 784 long dlqExpiration = deadLetterStrategy.getExpiration(); 785 if (dlqExpiration > 0) { 786 dlqExpiration += System.currentTimeMillis(); 787 } else { 788 stampAsExpired(message); 789 } 790 message.setExpiration(dlqExpiration); 791 if (!message.isPersistent()) { 792 message.setPersistent(true); 793 message.setProperty("originalDeliveryMode", "NON_PERSISTENT"); 794 } 795 if (poisonCause != null) { 796 message.setProperty(ActiveMQMessage.DLQ_DELIVERY_FAILURE_CAUSE_PROPERTY, 797 poisonCause.toString()); 798 } 799 // The original destination and transaction id do 800 // not get filled when the message is first sent, 801 // it is only populated if the message is routed to 802 // another destination like the DLQ 803 ConnectionContext adminContext = context; 804 if (context.getSecurityContext() == null || !context.getSecurityContext().isBrokerContext()) { 805 adminContext = BrokerSupport.getConnectionContext(this); 806 } 807 addDestination(adminContext, deadLetterDestination, false).getActiveMQDestination().setDLQ(true); 808 BrokerSupport.resendNoCopy(adminContext, message, deadLetterDestination); 809 return true; 810 } 811 } else { 812 LOG.debug("Dead Letter message with no DLQ strategy in place, message id: {}, destination: {}", message.getMessageId(), message.getDestination()); 813 } 814 } 815 } 816 } catch (Exception e) { 817 LOG.warn("Caught an exception sending to DLQ: {}", node, e); 818 } 819 820 return false; 821 } 822 823 @Override 824 public Broker getRoot() { 825 try { 826 return getBrokerService().getBroker(); 827 } catch (Exception e) { 828 LOG.error("Trying to get Root Broker", e); 829 throw new RuntimeException("The broker from the BrokerService should not throw an exception", e); 830 } 831 } 832 833 /** 834 * @return the broker sequence id 835 */ 836 @Override 837 public long getBrokerSequenceId() { 838 synchronized (sequenceGenerator) { 839 return sequenceGenerator.getNextSequenceId(); 840 } 841 } 842 843 @Override 844 public Scheduler getScheduler() { 845 return this.scheduler; 846 } 847 848 @Override 849 public ThreadPoolExecutor getExecutor() { 850 return this.executor; 851 } 852 853 @Override 854 public void processConsumerControl(ConsumerBrokerExchange consumerExchange, ConsumerControl control) { 855 ActiveMQDestination destination = control.getDestination(); 856 try { 857 getRegion(destination).processConsumerControl(consumerExchange, control); 858 } catch (JMSException jmse) { 859 LOG.warn("unmatched destination: {}, in consumerControl: {}", destination, control); 860 } 861 } 862 863 protected void addBrokerInClusterUpdate(BrokerInfo info) { 864 List<TransportConnector> connectors = this.brokerService.getTransportConnectors(); 865 for (TransportConnector connector : connectors) { 866 if (connector.isUpdateClusterClients()) { 867 connector.addPeerBroker(info); 868 connector.updateClientClusterInfo(); 869 } 870 } 871 } 872 873 protected void removeBrokerInClusterUpdate(BrokerInfo info) { 874 List<TransportConnector> connectors = this.brokerService.getTransportConnectors(); 875 for (TransportConnector connector : connectors) { 876 if (connector.isUpdateClusterClients() && connector.isUpdateClusterClientsOnRemove()) { 877 connector.removePeerBroker(info); 878 connector.updateClientClusterInfo(); 879 } 880 } 881 } 882 883 protected void purgeInactiveDestinations() { 884 inactiveDestinationsPurgeLock.writeLock().lock(); 885 try { 886 List<Destination> list = new ArrayList<Destination>(); 887 Map<ActiveMQDestination, Destination> map = getDestinationMap(); 888 if (isAllowTempAutoCreationOnSend()) { 889 map.putAll(tempQueueRegion.getDestinationMap()); 890 map.putAll(tempTopicRegion.getDestinationMap()); 891 } 892 long maxPurgedDests = this.brokerService.getMaxPurgedDestinationsPerSweep(); 893 long timeStamp = System.currentTimeMillis(); 894 for (Destination d : map.values()) { 895 d.markForGC(timeStamp); 896 if (d.canGC()) { 897 list.add(d); 898 if (maxPurgedDests > 0 && list.size() == maxPurgedDests) { 899 break; 900 } 901 } 902 } 903 904 if (!list.isEmpty()) { 905 ConnectionContext context = BrokerSupport.getConnectionContext(this); 906 context.setBroker(this); 907 908 for (Destination dest : list) { 909 Logger log = LOG; 910 if (dest instanceof BaseDestination) { 911 log = ((BaseDestination) dest).getLog(); 912 } 913 log.info("{} Inactive for longer than {} ms - removing ...", dest.getName(), dest.getInactiveTimeoutBeforeGC()); 914 try { 915 getRoot().removeDestination(context, dest.getActiveMQDestination(), isAllowTempAutoCreationOnSend() ? 1 : 0); 916 } catch (Throwable e) { 917 LOG.error("Failed to remove inactive destination {}", dest, e); 918 } 919 } 920 } 921 } finally { 922 inactiveDestinationsPurgeLock.writeLock().unlock(); 923 } 924 } 925 926 public boolean isAllowTempAutoCreationOnSend() { 927 return allowTempAutoCreationOnSend; 928 } 929 930 public void setAllowTempAutoCreationOnSend(boolean allowTempAutoCreationOnSend) { 931 this.allowTempAutoCreationOnSend = allowTempAutoCreationOnSend; 932 } 933 934 @Override 935 public void reapplyInterceptor() { 936 queueRegion.reapplyInterceptor(); 937 topicRegion.reapplyInterceptor(); 938 tempQueueRegion.reapplyInterceptor(); 939 tempTopicRegion.reapplyInterceptor(); 940 } 941}