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.advisory; 018 019import java.util.ArrayList; 020import java.util.Arrays; 021import java.util.Collection; 022import java.util.Collections; 023import java.util.Iterator; 024import java.util.LinkedHashMap; 025import java.util.Map; 026import java.util.Set; 027import java.util.concurrent.ConcurrentHashMap; 028import java.util.concurrent.ConcurrentMap; 029import java.util.concurrent.locks.ReentrantReadWriteLock; 030 031import org.apache.activemq.broker.Broker; 032import org.apache.activemq.broker.BrokerFilter; 033import org.apache.activemq.broker.BrokerService; 034import org.apache.activemq.broker.ConnectionContext; 035import org.apache.activemq.broker.ProducerBrokerExchange; 036import org.apache.activemq.broker.TransportConnector; 037import org.apache.activemq.broker.region.BaseDestination; 038import org.apache.activemq.broker.region.Destination; 039import org.apache.activemq.broker.region.DurableTopicSubscription; 040import org.apache.activemq.broker.region.MessageReference; 041import org.apache.activemq.broker.region.RegionBroker; 042import org.apache.activemq.broker.region.Subscription; 043import org.apache.activemq.broker.region.TopicRegion; 044import org.apache.activemq.broker.region.TopicSubscription; 045import org.apache.activemq.broker.region.virtual.VirtualDestination; 046import org.apache.activemq.broker.region.virtual.VirtualTopic; 047import org.apache.activemq.command.ActiveMQDestination; 048import org.apache.activemq.command.ActiveMQMessage; 049import org.apache.activemq.command.ActiveMQTopic; 050import org.apache.activemq.command.BrokerInfo; 051import org.apache.activemq.command.Command; 052import org.apache.activemq.command.ConnectionId; 053import org.apache.activemq.command.ConnectionInfo; 054import org.apache.activemq.command.ConsumerId; 055import org.apache.activemq.command.ConsumerInfo; 056import org.apache.activemq.command.DestinationInfo; 057import org.apache.activemq.command.Message; 058import org.apache.activemq.command.MessageId; 059import org.apache.activemq.command.ProducerId; 060import org.apache.activemq.command.ProducerInfo; 061import org.apache.activemq.command.RemoveSubscriptionInfo; 062import org.apache.activemq.command.SessionId; 063import org.apache.activemq.filter.DestinationPath; 064import org.apache.activemq.security.SecurityContext; 065import org.apache.activemq.state.ProducerState; 066import org.apache.activemq.usage.Usage; 067import org.apache.activemq.util.IdGenerator; 068import org.apache.activemq.util.LongSequenceGenerator; 069import org.apache.activemq.util.SubscriptionKey; 070import org.slf4j.Logger; 071import org.slf4j.LoggerFactory; 072 073/** 074 * This broker filter handles tracking the state of the broker for purposes of 075 * publishing advisory messages to advisory consumers. 076 */ 077public class AdvisoryBroker extends BrokerFilter { 078 079 private static final Logger LOG = LoggerFactory.getLogger(AdvisoryBroker.class); 080 private static final IdGenerator ID_GENERATOR = new IdGenerator(); 081 082 protected final ConcurrentMap<ConnectionId, ConnectionInfo> connections = new ConcurrentHashMap<ConnectionId, ConnectionInfo>(); 083 084 private final ReentrantReadWriteLock consumersLock = new ReentrantReadWriteLock(); 085 protected final Map<ConsumerId, ConsumerInfo> consumers = new LinkedHashMap<ConsumerId, ConsumerInfo>(); 086 087 /** 088 * This is a set to track all of the virtual destinations that have been added to the broker so 089 * they can be easily referenced later. 090 */ 091 protected final Set<VirtualDestination> virtualDestinations = Collections.newSetFromMap(new ConcurrentHashMap<VirtualDestination, Boolean>()); 092 /** 093 * This is a map to track all consumers that exist on the virtual destination so that we can fire 094 * an advisory later when they go away to remove the demand. 095 */ 096 protected final ConcurrentMap<ConsumerInfo, VirtualDestination> virtualDestinationConsumers = new ConcurrentHashMap<>(); 097 /** 098 * This is a map to track unique demand for the existence of a virtual destination so we make sure 099 * we don't send duplicate advisories. 100 */ 101 protected final ConcurrentMap<VirtualConsumerPair, ConsumerInfo> brokerConsumerDests = new ConcurrentHashMap<>(); 102 103 protected final ConcurrentMap<ProducerId, ProducerInfo> producers = new ConcurrentHashMap<ProducerId, ProducerInfo>(); 104 protected final ConcurrentMap<ActiveMQDestination, DestinationInfo> destinations = new ConcurrentHashMap<ActiveMQDestination, DestinationInfo>(); 105 protected final ConcurrentMap<BrokerInfo, ActiveMQMessage> networkBridges = new ConcurrentHashMap<BrokerInfo, ActiveMQMessage>(); 106 protected final ProducerId advisoryProducerId = new ProducerId(); 107 108 private final LongSequenceGenerator messageIdGenerator = new LongSequenceGenerator(); 109 110 private VirtualDestinationMatcher virtualDestinationMatcher = new DestinationFilterVirtualDestinationMatcher(); 111 112 public AdvisoryBroker(Broker next) { 113 super(next); 114 advisoryProducerId.setConnectionId(ID_GENERATOR.generateId()); 115 } 116 117 @Override 118 public void addConnection(ConnectionContext context, ConnectionInfo info) throws Exception { 119 super.addConnection(context, info); 120 121 ActiveMQTopic topic = AdvisorySupport.getConnectionAdvisoryTopic(); 122 // do not distribute passwords in advisory messages. usernames okay 123 ConnectionInfo copy = info.copy(); 124 copy.setPassword(""); 125 fireAdvisory(context, topic, copy); 126 connections.put(copy.getConnectionId(), copy); 127 } 128 129 @Override 130 public Subscription addConsumer(ConnectionContext context, ConsumerInfo info) throws Exception { 131 Subscription answer = super.addConsumer(context, info); 132 133 // Don't advise advisory topics. 134 if (!AdvisorySupport.isAdvisoryTopic(info.getDestination())) { 135 ActiveMQTopic topic = AdvisorySupport.getConsumerAdvisoryTopic(info.getDestination()); 136 consumersLock.writeLock().lock(); 137 try { 138 consumers.put(info.getConsumerId(), info); 139 140 //check if this is a consumer on a destination that matches a virtual destination 141 if (getBrokerService().isUseVirtualDestSubs()) { 142 for (VirtualDestination virtualDestination : virtualDestinations) { 143 if (virtualDestinationMatcher.matches(virtualDestination, info.getDestination())) { 144 fireVirtualDestinationAddAdvisory(context, info, info.getDestination(), virtualDestination); 145 } 146 } 147 } 148 } finally { 149 consumersLock.writeLock().unlock(); 150 } 151 fireConsumerAdvisory(context, info.getDestination(), topic, info); 152 } else { 153 // We need to replay all the previously collected state objects 154 // for this newly added consumer. 155 if (AdvisorySupport.isConnectionAdvisoryTopic(info.getDestination())) { 156 // Replay the connections. 157 for (Iterator<ConnectionInfo> iter = connections.values().iterator(); iter.hasNext(); ) { 158 ConnectionInfo value = iter.next(); 159 ActiveMQTopic topic = AdvisorySupport.getConnectionAdvisoryTopic(); 160 fireAdvisory(context, topic, value, info.getConsumerId()); 161 } 162 } 163 164 // We check here whether the Destination is Temporary Destination specific or not since we 165 // can avoid sending advisory messages to the consumer if it only wants Temporary Destination 166 // notifications. If its not just temporary destination related destinations then we have 167 // to send them all, a composite destination could want both. 168 if (AdvisorySupport.isTempDestinationAdvisoryTopic(info.getDestination())) { 169 // Replay the temporary destinations. 170 for (DestinationInfo destination : destinations.values()) { 171 if (destination.getDestination().isTemporary()) { 172 ActiveMQTopic topic = AdvisorySupport.getDestinationAdvisoryTopic(destination.getDestination()); 173 fireAdvisory(context, topic, destination, info.getConsumerId()); 174 } 175 } 176 } else if (AdvisorySupport.isDestinationAdvisoryTopic(info.getDestination())) { 177 // Replay all the destinations. 178 for (DestinationInfo destination : destinations.values()) { 179 ActiveMQTopic topic = AdvisorySupport.getDestinationAdvisoryTopic(destination.getDestination()); 180 fireAdvisory(context, topic, destination, info.getConsumerId()); 181 } 182 } 183 184 // Replay the producers. 185 if (AdvisorySupport.isProducerAdvisoryTopic(info.getDestination())) { 186 for (Iterator<ProducerInfo> iter = producers.values().iterator(); iter.hasNext(); ) { 187 ProducerInfo value = iter.next(); 188 ActiveMQTopic topic = AdvisorySupport.getProducerAdvisoryTopic(value.getDestination()); 189 fireProducerAdvisory(context, value.getDestination(), topic, value, info.getConsumerId()); 190 } 191 } 192 193 // Replay the consumers. 194 if (AdvisorySupport.isConsumerAdvisoryTopic(info.getDestination())) { 195 consumersLock.readLock().lock(); 196 try { 197 for (Iterator<ConsumerInfo> iter = consumers.values().iterator(); iter.hasNext(); ) { 198 ConsumerInfo value = iter.next(); 199 ActiveMQTopic topic = AdvisorySupport.getConsumerAdvisoryTopic(value.getDestination()); 200 fireConsumerAdvisory(context, value.getDestination(), topic, value, info.getConsumerId()); 201 } 202 } finally { 203 consumersLock.readLock().unlock(); 204 } 205 } 206 207 // Replay the virtual destination consumers. 208 if (AdvisorySupport.isVirtualDestinationConsumerAdvisoryTopic(info.getDestination())) { 209 for (Iterator<ConsumerInfo> iter = virtualDestinationConsumers.keySet().iterator(); iter.hasNext(); ) { 210 ConsumerInfo key = iter.next(); 211 ActiveMQTopic topic = AdvisorySupport.getVirtualDestinationConsumerAdvisoryTopic(key.getDestination()); 212 fireConsumerAdvisory(context, key.getDestination(), topic, key); 213 } 214 } 215 216 // Replay network bridges 217 if (AdvisorySupport.isNetworkBridgeAdvisoryTopic(info.getDestination())) { 218 for (Iterator<BrokerInfo> iter = networkBridges.keySet().iterator(); iter.hasNext(); ) { 219 BrokerInfo key = iter.next(); 220 ActiveMQTopic topic = AdvisorySupport.getNetworkBridgeAdvisoryTopic(); 221 fireAdvisory(context, topic, key, null, networkBridges.get(key)); 222 } 223 } 224 } 225 return answer; 226 } 227 228 @Override 229 public void addProducer(ConnectionContext context, ProducerInfo info) throws Exception { 230 super.addProducer(context, info); 231 232 //Verify destination is either non-null or that we want to advise anonymous producers on null destination 233 //Don't advise advisory topics. 234 if ((info.getDestination() != null || getBrokerService().isAnonymousProducerAdvisorySupport()) 235 && !AdvisorySupport.isAdvisoryTopic(info.getDestination())) { 236 ActiveMQTopic topic = AdvisorySupport.getProducerAdvisoryTopic(info.getDestination()); 237 fireProducerAdvisory(context, info.getDestination(), topic, info); 238 producers.put(info.getProducerId(), info); 239 } 240 } 241 242 @Override 243 public Destination addDestination(ConnectionContext context, ActiveMQDestination destination, boolean create) throws Exception { 244 Destination answer = super.addDestination(context, destination, create); 245 if (!AdvisorySupport.isAdvisoryTopic(destination)) { 246 //for queues, create demand if isUseVirtualDestSubsOnCreation is true 247 if (getBrokerService().isUseVirtualDestSubsOnCreation() && destination.isQueue()) { 248 //check if this new destination matches a virtual destination that exists 249 for (VirtualDestination virtualDestination : virtualDestinations) { 250 if (virtualDestinationMatcher.matches(virtualDestination, destination)) { 251 fireVirtualDestinationAddAdvisory(context, null, destination, virtualDestination); 252 } 253 } 254 } 255 256 DestinationInfo info = new DestinationInfo(context.getConnectionId(), DestinationInfo.ADD_OPERATION_TYPE, destination); 257 DestinationInfo previous = destinations.putIfAbsent(destination, info); 258 if (previous == null) { 259 ActiveMQTopic topic = AdvisorySupport.getDestinationAdvisoryTopic(destination); 260 fireAdvisory(context, topic, info); 261 } 262 } 263 return answer; 264 } 265 266 @Override 267 public void addDestinationInfo(ConnectionContext context, DestinationInfo info) throws Exception { 268 ActiveMQDestination destination = info.getDestination(); 269 next.addDestinationInfo(context, info); 270 271 if (!AdvisorySupport.isAdvisoryTopic(destination)) { 272 DestinationInfo previous = destinations.putIfAbsent(destination, info); 273 if (previous == null) { 274 ActiveMQTopic topic = AdvisorySupport.getDestinationAdvisoryTopic(destination); 275 fireAdvisory(context, topic, info); 276 } 277 } 278 } 279 280 @Override 281 public void removeDestination(ConnectionContext context, ActiveMQDestination destination, long timeout) throws Exception { 282 super.removeDestination(context, destination, timeout); 283 DestinationInfo info = destinations.remove(destination); 284 if (info != null) { 285 286 //on destination removal, remove all demand if using virtual dest subs 287 if (getBrokerService().isUseVirtualDestSubs()) { 288 for (ConsumerInfo consumerInfo : virtualDestinationConsumers.keySet()) { 289 //find all consumers for this virtual destination 290 VirtualDestination virtualDestination = virtualDestinationConsumers.get(consumerInfo); 291 292 //find a consumer that matches this virtualDest and destination 293 if (virtualDestinationMatcher.matches(virtualDestination, destination)) { 294 //in case of multiple matches 295 VirtualConsumerPair key = new VirtualConsumerPair(virtualDestination, destination); 296 ConsumerInfo i = brokerConsumerDests.get(key); 297 if (consumerInfo.equals(i) && brokerConsumerDests.remove(key) != null) { 298 LOG.debug("Virtual consumer pair removed: {} for consumer: {} ", key, i); 299 fireVirtualDestinationRemoveAdvisory(context, consumerInfo); 300 break; 301 } 302 } 303 } 304 } 305 306 // ensure we don't modify (and loose/overwrite) an in-flight add advisory, so duplicate 307 info = info.copy(); 308 info.setDestination(destination); 309 info.setOperationType(DestinationInfo.REMOVE_OPERATION_TYPE); 310 ActiveMQTopic topic = AdvisorySupport.getDestinationAdvisoryTopic(destination); 311 fireAdvisory(context, topic, info); 312 ActiveMQTopic[] advisoryDestinations = AdvisorySupport.getAllDestinationAdvisoryTopics(destination); 313 for (ActiveMQTopic advisoryDestination : advisoryDestinations) { 314 try { 315 next.removeDestination(context, advisoryDestination, -1); 316 } catch (Exception expectedIfDestinationDidNotExistYet) { 317 } 318 } 319 } 320 } 321 322 @Override 323 public void removeDestinationInfo(ConnectionContext context, DestinationInfo destInfo) throws Exception { 324 super.removeDestinationInfo(context, destInfo); 325 DestinationInfo info = destinations.remove(destInfo.getDestination()); 326 if (info != null) { 327 // ensure we don't modify (and loose/overwrite) an in-flight add advisory, so duplicate 328 info = info.copy(); 329 info.setDestination(destInfo.getDestination()); 330 info.setOperationType(DestinationInfo.REMOVE_OPERATION_TYPE); 331 ActiveMQTopic topic = AdvisorySupport.getDestinationAdvisoryTopic(destInfo.getDestination()); 332 fireAdvisory(context, topic, info); 333 ActiveMQTopic[] advisoryDestinations = AdvisorySupport.getAllDestinationAdvisoryTopics(destInfo.getDestination()); 334 for (ActiveMQTopic advisoryDestination : advisoryDestinations) { 335 try { 336 next.removeDestination(context, advisoryDestination, -1); 337 } catch (Exception expectedIfDestinationDidNotExistYet) { 338 } 339 } 340 } 341 } 342 343 @Override 344 public void removeConnection(ConnectionContext context, ConnectionInfo info, Throwable error) throws Exception { 345 super.removeConnection(context, info, error); 346 347 ActiveMQTopic topic = AdvisorySupport.getConnectionAdvisoryTopic(); 348 fireAdvisory(context, topic, info.createRemoveCommand()); 349 connections.remove(info.getConnectionId()); 350 } 351 352 @Override 353 public void removeConsumer(ConnectionContext context, ConsumerInfo info) throws Exception { 354 super.removeConsumer(context, info); 355 356 // Don't advise advisory topics. 357 ActiveMQDestination dest = info.getDestination(); 358 if (!AdvisorySupport.isAdvisoryTopic(dest)) { 359 ActiveMQTopic topic = AdvisorySupport.getConsumerAdvisoryTopic(dest); 360 consumersLock.writeLock().lock(); 361 try { 362 consumers.remove(info.getConsumerId()); 363 364 //remove the demand for this consumer if it matches a virtual destination 365 if(getBrokerService().isUseVirtualDestSubs()) { 366 fireVirtualDestinationRemoveAdvisory(context, info); 367 } 368 } finally { 369 consumersLock.writeLock().unlock(); 370 } 371 if (!dest.isTemporary() || destinations.containsKey(dest)) { 372 fireConsumerAdvisory(context, dest, topic, info.createRemoveCommand()); 373 } 374 } 375 } 376 377 @Override 378 public void removeSubscription(ConnectionContext context, RemoveSubscriptionInfo info) throws Exception { 379 SubscriptionKey key = new SubscriptionKey(context.getClientId(), info.getSubscriptionName()); 380 381 RegionBroker regionBroker = null; 382 if (next instanceof RegionBroker) { 383 regionBroker = (RegionBroker) next; 384 } else { 385 BrokerService service = next.getBrokerService(); 386 regionBroker = (RegionBroker) service.getRegionBroker(); 387 } 388 389 if (regionBroker == null) { 390 LOG.warn("Cannot locate a RegionBroker instance to pass along the removeSubscription call"); 391 throw new IllegalStateException("No RegionBroker found."); 392 } 393 394 DurableTopicSubscription sub = ((TopicRegion) regionBroker.getTopicRegion()).getDurableSubscription(key); 395 396 super.removeSubscription(context, info); 397 398 if (sub == null) { 399 LOG.warn("We cannot send an advisory message for a durable sub removal when we don't know about the durable sub"); 400 return; 401 } 402 403 ActiveMQDestination dest = sub.getConsumerInfo().getDestination(); 404 405 // Don't advise advisory topics. 406 if (!AdvisorySupport.isAdvisoryTopic(dest)) { 407 ActiveMQTopic topic = AdvisorySupport.getConsumerAdvisoryTopic(dest); 408 fireConsumerAdvisory(context, dest, topic, info); 409 } 410 411 } 412 413 @Override 414 public void removeProducer(ConnectionContext context, ProducerInfo info) throws Exception { 415 super.removeProducer(context, info); 416 417 //Verify destination is either non-null or that we want to advise anonymous producers on null destination 418 //Don't advise advisory topics. 419 ActiveMQDestination dest = info.getDestination(); 420 if ((dest != null || getBrokerService().isAnonymousProducerAdvisorySupport()) && !AdvisorySupport.isAdvisoryTopic(dest)) { 421 ActiveMQTopic topic = AdvisorySupport.getProducerAdvisoryTopic(dest); 422 producers.remove(info.getProducerId()); 423 if (dest == null || !dest.isTemporary() || destinations.containsKey(dest)) { 424 fireProducerAdvisory(context, dest, topic, info.createRemoveCommand()); 425 } 426 } 427 } 428 429 @Override 430 public void messageExpired(ConnectionContext context, MessageReference messageReference, Subscription subscription) { 431 super.messageExpired(context, messageReference, subscription); 432 try { 433 if (!messageReference.isAdvisory()) { 434 BaseDestination baseDestination = (BaseDestination) messageReference.getMessage().getRegionDestination(); 435 ActiveMQTopic topic = AdvisorySupport.getExpiredMessageTopic(baseDestination.getActiveMQDestination()); 436 Message payload = messageReference.getMessage().copy(); 437 if (!baseDestination.isIncludeBodyForAdvisory()) { 438 payload.clearBody(); 439 } 440 ActiveMQMessage advisoryMessage = new ActiveMQMessage(); 441 advisoryMessage.setStringProperty(AdvisorySupport.MSG_PROPERTY_MESSAGE_ID, payload.getMessageId().toString()); 442 fireAdvisory(context, topic, payload, null, advisoryMessage); 443 } 444 } catch (Exception e) { 445 handleFireFailure("expired", e); 446 } 447 } 448 449 @Override 450 public void messageConsumed(ConnectionContext context, MessageReference messageReference) { 451 super.messageConsumed(context, messageReference); 452 try { 453 if (!messageReference.isAdvisory()) { 454 BaseDestination baseDestination = (BaseDestination) messageReference.getMessage().getRegionDestination(); 455 ActiveMQTopic topic = AdvisorySupport.getMessageConsumedAdvisoryTopic(baseDestination.getActiveMQDestination()); 456 Message payload = messageReference.getMessage().copy(); 457 if (!baseDestination.isIncludeBodyForAdvisory()) { 458 payload.clearBody(); 459 } 460 ActiveMQMessage advisoryMessage = new ActiveMQMessage(); 461 advisoryMessage.setStringProperty(AdvisorySupport.MSG_PROPERTY_MESSAGE_ID, payload.getMessageId().toString()); 462 advisoryMessage.setStringProperty(AdvisorySupport.MSG_PROPERTY_DESTINATION, baseDestination.getActiveMQDestination().getQualifiedName()); 463 fireAdvisory(context, topic, payload, null, advisoryMessage); 464 } 465 } catch (Exception e) { 466 handleFireFailure("consumed", e); 467 } 468 } 469 470 @Override 471 public void messageDelivered(ConnectionContext context, MessageReference messageReference) { 472 super.messageDelivered(context, messageReference); 473 try { 474 if (!messageReference.isAdvisory()) { 475 BaseDestination baseDestination = (BaseDestination) messageReference.getMessage().getRegionDestination(); 476 ActiveMQTopic topic = AdvisorySupport.getMessageDeliveredAdvisoryTopic(baseDestination.getActiveMQDestination()); 477 Message payload = messageReference.getMessage().copy(); 478 if (!baseDestination.isIncludeBodyForAdvisory()) { 479 payload.clearBody(); 480 } 481 ActiveMQMessage advisoryMessage = new ActiveMQMessage(); 482 advisoryMessage.setStringProperty(AdvisorySupport.MSG_PROPERTY_MESSAGE_ID, payload.getMessageId().toString()); 483 advisoryMessage.setStringProperty(AdvisorySupport.MSG_PROPERTY_DESTINATION, baseDestination.getActiveMQDestination().getQualifiedName()); 484 fireAdvisory(context, topic, payload, null, advisoryMessage); 485 } 486 } catch (Exception e) { 487 handleFireFailure("delivered", e); 488 } 489 } 490 491 @Override 492 public void messageDiscarded(ConnectionContext context, Subscription sub, MessageReference messageReference) { 493 super.messageDiscarded(context, sub, messageReference); 494 try { 495 if (!messageReference.isAdvisory()) { 496 BaseDestination baseDestination = (BaseDestination) messageReference.getMessage().getRegionDestination(); 497 ActiveMQTopic topic = AdvisorySupport.getMessageDiscardedAdvisoryTopic(baseDestination.getActiveMQDestination()); 498 Message payload = messageReference.getMessage().copy(); 499 if (!baseDestination.isIncludeBodyForAdvisory()) { 500 payload.clearBody(); 501 } 502 ActiveMQMessage advisoryMessage = new ActiveMQMessage(); 503 if (sub instanceof TopicSubscription) { 504 advisoryMessage.setIntProperty(AdvisorySupport.MSG_PROPERTY_DISCARDED_COUNT, ((TopicSubscription) sub).discarded()); 505 } 506 advisoryMessage.setStringProperty(AdvisorySupport.MSG_PROPERTY_MESSAGE_ID, payload.getMessageId().toString()); 507 advisoryMessage.setStringProperty(AdvisorySupport.MSG_PROPERTY_CONSUMER_ID, sub.getConsumerInfo().getConsumerId().toString()); 508 advisoryMessage.setStringProperty(AdvisorySupport.MSG_PROPERTY_DESTINATION, baseDestination.getActiveMQDestination().getQualifiedName()); 509 510 fireAdvisory(context, topic, payload, null, advisoryMessage); 511 } 512 } catch (Exception e) { 513 handleFireFailure("discarded", e); 514 } 515 } 516 517 @Override 518 public void slowConsumer(ConnectionContext context, Destination destination, Subscription subs) { 519 super.slowConsumer(context, destination, subs); 520 try { 521 if (!AdvisorySupport.isAdvisoryTopic(destination.getActiveMQDestination())) { 522 ActiveMQTopic topic = AdvisorySupport.getSlowConsumerAdvisoryTopic(destination.getActiveMQDestination()); 523 ActiveMQMessage advisoryMessage = new ActiveMQMessage(); 524 advisoryMessage.setStringProperty(AdvisorySupport.MSG_PROPERTY_CONSUMER_ID, subs.getConsumerInfo().getConsumerId().toString()); 525 fireAdvisory(context, topic, subs.getConsumerInfo(), null, advisoryMessage); 526 } 527 } catch (Exception e) { 528 handleFireFailure("slow consumer", e); 529 } 530 } 531 532 @Override 533 public void fastProducer(ConnectionContext context, ProducerInfo producerInfo, ActiveMQDestination destination) { 534 super.fastProducer(context, producerInfo, destination); 535 try { 536 if (!AdvisorySupport.isAdvisoryTopic(destination)) { 537 ActiveMQTopic topic = AdvisorySupport.getFastProducerAdvisoryTopic(destination); 538 ActiveMQMessage advisoryMessage = new ActiveMQMessage(); 539 advisoryMessage.setStringProperty(AdvisorySupport.MSG_PROPERTY_PRODUCER_ID, producerInfo.getProducerId().toString()); 540 fireAdvisory(context, topic, producerInfo, null, advisoryMessage); 541 } 542 } catch (Exception e) { 543 handleFireFailure("fast producer", e); 544 } 545 } 546 547 private final IdGenerator connectionIdGenerator = new IdGenerator("advisory"); 548 private final LongSequenceGenerator sessionIdGenerator = new LongSequenceGenerator(); 549 private final LongSequenceGenerator consumerIdGenerator = new LongSequenceGenerator(); 550 551 @Override 552 public void virtualDestinationAdded(ConnectionContext context, 553 VirtualDestination virtualDestination) { 554 super.virtualDestinationAdded(context, virtualDestination); 555 556 if (virtualDestinations.add(virtualDestination)) { 557 LOG.debug("Virtual destination added: {}", virtualDestination); 558 try { 559 // Don't advise advisory topics. 560 if (!AdvisorySupport.isAdvisoryTopic(virtualDestination.getVirtualDestination())) { 561 562 //create demand for consumers on virtual destinations 563 consumersLock.readLock().lock(); 564 try { 565 //loop through existing destinations to see if any match this newly 566 //created virtual destination 567 if (getBrokerService().isUseVirtualDestSubsOnCreation()) { 568 //for matches that are a queue, fire an advisory for demand 569 for (ActiveMQDestination destination : destinations.keySet()) { 570 if(destination.isQueue()) { 571 if (virtualDestinationMatcher.matches(virtualDestination, destination)) { 572 fireVirtualDestinationAddAdvisory(context, null, destination, virtualDestination); 573 } 574 } 575 } 576 } 577 578 //loop through existing consumers to see if any of them are consuming on a destination 579 //that matches the new virtual destination 580 for (Iterator<ConsumerInfo> iter = consumers.values().iterator(); iter.hasNext(); ) { 581 ConsumerInfo info = iter.next(); 582 if (virtualDestinationMatcher.matches(virtualDestination, info.getDestination())) { 583 fireVirtualDestinationAddAdvisory(context, info, info.getDestination(), virtualDestination); 584 } 585 } 586 } finally { 587 consumersLock.readLock().unlock(); 588 } 589 } 590 } catch (Exception e) { 591 handleFireFailure("virtualDestinationAdded", e); 592 } 593 } 594 } 595 596 private void fireVirtualDestinationAddAdvisory(ConnectionContext context, ConsumerInfo info, ActiveMQDestination activeMQDest, 597 VirtualDestination virtualDestination) throws Exception { 598 //if no consumer info, we need to create one - this is the case when an advisory is fired 599 //because of the existence of a destination matching a virtual destination 600 if (info == null) { 601 602 //store the virtual destination and the activeMQDestination as a pair so that we can keep track 603 //of all matching forwarded destinations that caused demand 604 VirtualConsumerPair pair = new VirtualConsumerPair(virtualDestination, activeMQDest); 605 if (brokerConsumerDests.get(pair) == null) { 606 ConnectionId connectionId = new ConnectionId(connectionIdGenerator.generateId()); 607 SessionId sessionId = new SessionId(connectionId, sessionIdGenerator.getNextSequenceId()); 608 ConsumerId consumerId = new ConsumerId(sessionId, consumerIdGenerator.getNextSequenceId()); 609 info = new ConsumerInfo(consumerId); 610 611 if(brokerConsumerDests.putIfAbsent(pair, info) == null) { 612 LOG.debug("Virtual consumer pair added: {} for consumer: {} ", pair, info); 613 setConsumerInfoVirtualDest(info, virtualDestination, activeMQDest); 614 ActiveMQTopic topic = AdvisorySupport.getVirtualDestinationConsumerAdvisoryTopic(info.getDestination()); 615 616 if (virtualDestinationConsumers.putIfAbsent(info, virtualDestination) == null) { 617 LOG.debug("Virtual consumer added: {}, for virtual destination: {}", info, virtualDestination); 618 fireConsumerAdvisory(context, info.getDestination(), topic, info); 619 } 620 } 621 } 622 //this is the case of a real consumer coming online 623 } else { 624 info = info.copy(); 625 setConsumerInfoVirtualDest(info, virtualDestination, activeMQDest); 626 ActiveMQTopic topic = AdvisorySupport.getVirtualDestinationConsumerAdvisoryTopic(info.getDestination()); 627 628 if (virtualDestinationConsumers.putIfAbsent(info, virtualDestination) == null) { 629 LOG.debug("Virtual consumer added: {}, for virtual destination: {}", info, virtualDestination); 630 fireConsumerAdvisory(context, info.getDestination(), topic, info); 631 } 632 } 633 } 634 635 /** 636 * Sets the virtual destination on the ConsumerInfo 637 * If this is a VirtualTopic then the destination used will be the actual topic subscribed 638 * to in order to track demand properly 639 * 640 * @param info 641 * @param virtualDestination 642 * @param activeMQDest 643 */ 644 private void setConsumerInfoVirtualDest(ConsumerInfo info, VirtualDestination virtualDestination, ActiveMQDestination activeMQDest) { 645 info.setDestination(virtualDestination.getVirtualDestination()); 646 if (virtualDestination instanceof VirtualTopic) { 647 VirtualTopic vt = (VirtualTopic) virtualDestination; 648 String prefix = vt.getPrefix() != null ? vt.getPrefix() : ""; 649 String postfix = vt.getPostfix() != null ? vt.getPostfix() : ""; 650 if (prefix.endsWith(".")) { 651 prefix = prefix.substring(0, prefix.length() - 1); 652 } 653 if (postfix.startsWith(".")) { 654 postfix = postfix.substring(1, postfix.length()); 655 } 656 ActiveMQDestination prefixDestination = prefix.length() > 0 ? new ActiveMQTopic(prefix) : null; 657 ActiveMQDestination postfixDestination = postfix.length() > 0 ? new ActiveMQTopic(postfix) : null; 658 659 String[] prefixPaths = prefixDestination != null ? prefixDestination.getDestinationPaths() : new String[] {}; 660 String[] activeMQDestPaths = activeMQDest.getDestinationPaths(); 661 String[] postfixPaths = postfixDestination != null ? postfixDestination.getDestinationPaths() : new String[] {}; 662 663 //sanity check 664 if (activeMQDestPaths.length > prefixPaths.length + postfixPaths.length) { 665 String[] topicPath = Arrays.copyOfRange(activeMQDestPaths, 0 + prefixPaths.length, 666 activeMQDestPaths.length - postfixPaths.length); 667 668 ActiveMQTopic newTopic = new ActiveMQTopic(DestinationPath.toString(topicPath)); 669 info.setDestination(newTopic); 670 } 671 } 672 } 673 674 @Override 675 public void virtualDestinationRemoved(ConnectionContext context, 676 VirtualDestination virtualDestination) { 677 super.virtualDestinationRemoved(context, virtualDestination); 678 679 if (virtualDestinations.remove(virtualDestination)) { 680 LOG.debug("Virtual destination removed: {}", virtualDestination); 681 try { 682 consumersLock.readLock().lock(); 683 try { 684 // remove the demand created by the addition of the virtual destination 685 if (getBrokerService().isUseVirtualDestSubsOnCreation()) { 686 if (!AdvisorySupport.isAdvisoryTopic(virtualDestination.getVirtualDestination())) { 687 for (ConsumerInfo info : virtualDestinationConsumers.keySet()) { 688 //find all consumers for this virtual destination 689 if (virtualDestinationConsumers.get(info).equals(virtualDestination)) { 690 fireVirtualDestinationRemoveAdvisory(context, info); 691 692 //check consumers created for the existence of a destination to see if they 693 //match the consumerinfo and clean up 694 for (VirtualConsumerPair activeMQDest : brokerConsumerDests.keySet()) { 695 ConsumerInfo i = brokerConsumerDests.get(activeMQDest); 696 if (info.equals(i) && brokerConsumerDests.remove(activeMQDest) != null) { 697 LOG.debug("Virtual consumer pair removed: {} for consumer: {} ", activeMQDest, i); 698 } 699 } 700 } 701 702 } 703 } 704 } 705 } finally { 706 consumersLock.readLock().unlock(); 707 } 708 } catch (Exception e) { 709 handleFireFailure("virtualDestinationAdded", e); 710 } 711 } 712 } 713 714 private void fireVirtualDestinationRemoveAdvisory(ConnectionContext context, 715 ConsumerInfo info) throws Exception { 716 717 VirtualDestination virtualDestination = virtualDestinationConsumers.remove(info); 718 if (virtualDestination != null) { 719 LOG.debug("Virtual consumer removed: {}, for virtual destination: {}", info, virtualDestination); 720 ActiveMQTopic topic = AdvisorySupport.getVirtualDestinationConsumerAdvisoryTopic(virtualDestination.getVirtualDestination()); 721 722 ActiveMQDestination dest = info.getDestination(); 723 724 if (!dest.isTemporary() || destinations.containsKey(dest)) { 725 fireConsumerAdvisory(context, dest, topic, info.createRemoveCommand()); 726 } 727 } 728 } 729 730 @Override 731 public void isFull(ConnectionContext context, Destination destination, Usage<?> usage) { 732 super.isFull(context, destination, usage); 733 if (AdvisorySupport.isAdvisoryTopic(destination.getActiveMQDestination()) == false) { 734 try { 735 736 ActiveMQTopic topic = AdvisorySupport.getFullAdvisoryTopic(destination.getActiveMQDestination()); 737 ActiveMQMessage advisoryMessage = new ActiveMQMessage(); 738 advisoryMessage.setStringProperty(AdvisorySupport.MSG_PROPERTY_USAGE_NAME, usage.getName()); 739 advisoryMessage.setLongProperty(AdvisorySupport.MSG_PROPERTY_USAGE_COUNT, usage.getUsage()); 740 fireAdvisory(context, topic, null, null, advisoryMessage); 741 742 } catch (Exception e) { 743 handleFireFailure("is full", e); 744 } 745 } 746 } 747 748 @Override 749 public void nowMasterBroker() { 750 super.nowMasterBroker(); 751 try { 752 ActiveMQTopic topic = AdvisorySupport.getMasterBrokerAdvisoryTopic(); 753 ActiveMQMessage advisoryMessage = new ActiveMQMessage(); 754 ConnectionContext context = new ConnectionContext(); 755 context.setSecurityContext(SecurityContext.BROKER_SECURITY_CONTEXT); 756 context.setBroker(getBrokerService().getBroker()); 757 fireAdvisory(context, topic, null, null, advisoryMessage); 758 } catch (Exception e) { 759 handleFireFailure("now master broker", e); 760 } 761 } 762 763 @Override 764 public boolean sendToDeadLetterQueue(ConnectionContext context, MessageReference messageReference, 765 Subscription subscription, Throwable poisonCause) { 766 boolean wasDLQd = super.sendToDeadLetterQueue(context, messageReference, subscription, poisonCause); 767 if (wasDLQd) { 768 try { 769 if (!messageReference.isAdvisory()) { 770 BaseDestination baseDestination = (BaseDestination) messageReference.getMessage().getRegionDestination(); 771 ActiveMQTopic topic = AdvisorySupport.getMessageDLQdAdvisoryTopic(baseDestination.getActiveMQDestination()); 772 Message payload = messageReference.getMessage().copy(); 773 if (!baseDestination.isIncludeBodyForAdvisory()) { 774 payload.clearBody(); 775 } 776 fireAdvisory(context, topic, payload); 777 } 778 } catch (Exception e) { 779 handleFireFailure("add to DLQ", e); 780 } 781 } 782 783 return wasDLQd; 784 } 785 786 @Override 787 public void networkBridgeStarted(BrokerInfo brokerInfo, boolean createdByDuplex, String remoteIp) { 788 try { 789 if (brokerInfo != null) { 790 ActiveMQMessage advisoryMessage = new ActiveMQMessage(); 791 advisoryMessage.setBooleanProperty("started", true); 792 advisoryMessage.setBooleanProperty("createdByDuplex", createdByDuplex); 793 advisoryMessage.setStringProperty("remoteIp", remoteIp); 794 networkBridges.putIfAbsent(brokerInfo, advisoryMessage); 795 796 ActiveMQTopic topic = AdvisorySupport.getNetworkBridgeAdvisoryTopic(); 797 798 ConnectionContext context = new ConnectionContext(); 799 context.setSecurityContext(SecurityContext.BROKER_SECURITY_CONTEXT); 800 context.setBroker(getBrokerService().getBroker()); 801 fireAdvisory(context, topic, brokerInfo, null, advisoryMessage); 802 } 803 } catch (Exception e) { 804 handleFireFailure("network bridge started", e); 805 } 806 } 807 808 @Override 809 public void networkBridgeStopped(BrokerInfo brokerInfo) { 810 try { 811 if (brokerInfo != null) { 812 ActiveMQMessage advisoryMessage = new ActiveMQMessage(); 813 advisoryMessage.setBooleanProperty("started", false); 814 networkBridges.remove(brokerInfo); 815 816 ActiveMQTopic topic = AdvisorySupport.getNetworkBridgeAdvisoryTopic(); 817 818 ConnectionContext context = new ConnectionContext(); 819 context.setSecurityContext(SecurityContext.BROKER_SECURITY_CONTEXT); 820 context.setBroker(getBrokerService().getBroker()); 821 fireAdvisory(context, topic, brokerInfo, null, advisoryMessage); 822 } 823 } catch (Exception e) { 824 handleFireFailure("network bridge stopped", e); 825 } 826 } 827 828 private void handleFireFailure(String message, Throwable cause) { 829 LOG.warn("Failed to fire {} advisory, reason: {}", message, cause); 830 LOG.debug("{} detail: {}", message, cause, cause); 831 } 832 833 protected void fireAdvisory(ConnectionContext context, ActiveMQTopic topic, Command command) throws Exception { 834 fireAdvisory(context, topic, command, null); 835 } 836 837 protected void fireAdvisory(ConnectionContext context, ActiveMQTopic topic, Command command, ConsumerId targetConsumerId) throws Exception { 838 ActiveMQMessage advisoryMessage = new ActiveMQMessage(); 839 fireAdvisory(context, topic, command, targetConsumerId, advisoryMessage); 840 } 841 842 protected void fireConsumerAdvisory(ConnectionContext context, ActiveMQDestination consumerDestination, ActiveMQTopic topic, Command command) throws Exception { 843 fireConsumerAdvisory(context, consumerDestination, topic, command, null); 844 } 845 846 protected void fireConsumerAdvisory(ConnectionContext context, ActiveMQDestination consumerDestination, ActiveMQTopic topic, Command command, ConsumerId targetConsumerId) throws Exception { 847 ActiveMQMessage advisoryMessage = new ActiveMQMessage(); 848 int count = 0; 849 Set<Destination> set = getDestinations(consumerDestination); 850 if (set != null) { 851 for (Destination dest : set) { 852 count += dest.getDestinationStatistics().getConsumers().getCount(); 853 } 854 } 855 advisoryMessage.setIntProperty(AdvisorySupport.MSG_PROPERTY_CONSUMER_COUNT, count); 856 857 fireAdvisory(context, topic, command, targetConsumerId, advisoryMessage); 858 } 859 860 protected void fireProducerAdvisory(ConnectionContext context, ActiveMQDestination producerDestination, ActiveMQTopic topic, Command command) throws Exception { 861 fireProducerAdvisory(context, producerDestination, topic, command, null); 862 } 863 864 protected void fireProducerAdvisory(ConnectionContext context, ActiveMQDestination producerDestination, ActiveMQTopic topic, Command command, ConsumerId targetConsumerId) throws Exception { 865 ActiveMQMessage advisoryMessage = new ActiveMQMessage(); 866 int count = 0; 867 if (producerDestination != null) { 868 Set<Destination> set = getDestinations(producerDestination); 869 if (set != null) { 870 for (Destination dest : set) { 871 count += dest.getDestinationStatistics().getProducers().getCount(); 872 } 873 } 874 } 875 advisoryMessage.setIntProperty("producerCount", count); 876 fireAdvisory(context, topic, command, targetConsumerId, advisoryMessage); 877 } 878 879 public void fireAdvisory(ConnectionContext context, ActiveMQTopic topic, Command command, ConsumerId targetConsumerId, ActiveMQMessage advisoryMessage) throws Exception { 880 //set properties 881 advisoryMessage.setStringProperty(AdvisorySupport.MSG_PROPERTY_ORIGIN_BROKER_NAME, getBrokerName()); 882 String id = getBrokerId() != null ? getBrokerId().getValue() : "NOT_SET"; 883 advisoryMessage.setStringProperty(AdvisorySupport.MSG_PROPERTY_ORIGIN_BROKER_ID, id); 884 885 String url = getBrokerService().getVmConnectorURI().toString(); 886 //try and find the URL on the transport connector and use if it exists else 887 //try and find a default URL 888 if (context.getConnector() instanceof TransportConnector 889 && ((TransportConnector) context.getConnector()).getPublishableConnectString() != null) { 890 url = ((TransportConnector) context.getConnector()).getPublishableConnectString(); 891 } else if (getBrokerService().getDefaultSocketURIString() != null) { 892 url = getBrokerService().getDefaultSocketURIString(); 893 } 894 advisoryMessage.setStringProperty(AdvisorySupport.MSG_PROPERTY_ORIGIN_BROKER_URL, url); 895 896 //set the data structure 897 advisoryMessage.setDataStructure(command); 898 advisoryMessage.setPersistent(false); 899 advisoryMessage.setType(AdvisorySupport.ADIVSORY_MESSAGE_TYPE); 900 advisoryMessage.setMessageId(new MessageId(advisoryProducerId, messageIdGenerator.getNextSequenceId())); 901 advisoryMessage.setTargetConsumerId(targetConsumerId); 902 advisoryMessage.setDestination(topic); 903 advisoryMessage.setResponseRequired(false); 904 advisoryMessage.setProducerId(advisoryProducerId); 905 boolean originalFlowControl = context.isProducerFlowControl(); 906 final ProducerBrokerExchange producerExchange = new ProducerBrokerExchange(); 907 producerExchange.setConnectionContext(context); 908 producerExchange.setMutable(true); 909 producerExchange.setProducerState(new ProducerState(new ProducerInfo())); 910 try { 911 context.setProducerFlowControl(false); 912 next.send(producerExchange, advisoryMessage); 913 } finally { 914 context.setProducerFlowControl(originalFlowControl); 915 } 916 } 917 918 public Map<ConnectionId, ConnectionInfo> getAdvisoryConnections() { 919 return connections; 920 } 921 922 public Collection<ConsumerInfo> getAdvisoryConsumers() { 923 consumersLock.readLock().lock(); 924 try { 925 return new ArrayList<ConsumerInfo>(consumers.values()); 926 } finally { 927 consumersLock.readLock().unlock(); 928 } 929 } 930 931 public Map<ProducerId, ProducerInfo> getAdvisoryProducers() { 932 return producers; 933 } 934 935 public Map<ActiveMQDestination, DestinationInfo> getAdvisoryDestinations() { 936 return destinations; 937 } 938 939 public ConcurrentMap<ConsumerInfo, VirtualDestination> getVirtualDestinationConsumers() { 940 return virtualDestinationConsumers; 941 } 942 943 private class VirtualConsumerPair { 944 private final VirtualDestination virtualDestination; 945 946 //destination that matches this virtualDestination as part target 947 //this is so we can keep track of more than one destination that might 948 //match the virtualDestination and cause demand 949 private final ActiveMQDestination activeMQDestination; 950 951 public VirtualConsumerPair(VirtualDestination virtualDestination, 952 ActiveMQDestination activeMQDestination) { 953 super(); 954 this.virtualDestination = virtualDestination; 955 this.activeMQDestination = activeMQDestination; 956 } 957 958 @Override 959 public int hashCode() { 960 final int prime = 31; 961 int result = 1; 962 result = prime * result + getOuterType().hashCode(); 963 result = prime 964 * result 965 + ((activeMQDestination == null) ? 0 : activeMQDestination 966 .hashCode()); 967 result = prime 968 * result 969 + ((virtualDestination == null) ? 0 : virtualDestination 970 .hashCode()); 971 return result; 972 } 973 974 @Override 975 public boolean equals(Object obj) { 976 if (this == obj) 977 return true; 978 if (obj == null) 979 return false; 980 if (getClass() != obj.getClass()) 981 return false; 982 VirtualConsumerPair other = (VirtualConsumerPair) obj; 983 if (!getOuterType().equals(other.getOuterType())) 984 return false; 985 if (activeMQDestination == null) { 986 if (other.activeMQDestination != null) 987 return false; 988 } else if (!activeMQDestination.equals(other.activeMQDestination)) 989 return false; 990 if (virtualDestination == null) { 991 if (other.virtualDestination != null) 992 return false; 993 } else if (!virtualDestination.equals(other.virtualDestination)) 994 return false; 995 return true; 996 } 997 998 @Override 999 public String toString() { 1000 return "VirtualConsumerPair [virtualDestination=" + virtualDestination + ", activeMQDestination=" 1001 + activeMQDestination + "]"; 1002 } 1003 1004 private AdvisoryBroker getOuterType() { 1005 return AdvisoryBroker.this; 1006 } 1007 } 1008}