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