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.network; 018 019import java.io.IOException; 020import java.security.GeneralSecurityException; 021import java.security.cert.X509Certificate; 022import java.util.Arrays; 023import java.util.Collection; 024import java.util.Iterator; 025import java.util.List; 026import java.util.Properties; 027import java.util.concurrent.ConcurrentHashMap; 028import java.util.concurrent.CountDownLatch; 029import java.util.concurrent.ExecutionException; 030import java.util.concurrent.ExecutorService; 031import java.util.concurrent.Executors; 032import java.util.concurrent.Future; 033import java.util.concurrent.TimeUnit; 034import java.util.concurrent.TimeoutException; 035import java.util.concurrent.atomic.AtomicBoolean; 036import java.util.concurrent.atomic.AtomicLong; 037 038import javax.management.ObjectName; 039 040import org.apache.activemq.DestinationDoesNotExistException; 041import org.apache.activemq.Service; 042import org.apache.activemq.advisory.AdvisoryBroker; 043import org.apache.activemq.advisory.AdvisorySupport; 044import org.apache.activemq.broker.BrokerService; 045import org.apache.activemq.broker.BrokerServiceAware; 046import org.apache.activemq.broker.ConnectionContext; 047import org.apache.activemq.broker.TransportConnection; 048import org.apache.activemq.broker.region.AbstractRegion; 049import org.apache.activemq.broker.region.DurableTopicSubscription; 050import org.apache.activemq.broker.region.Region; 051import org.apache.activemq.broker.region.RegionBroker; 052import org.apache.activemq.broker.region.Subscription; 053import org.apache.activemq.broker.region.policy.PolicyEntry; 054import org.apache.activemq.command.ActiveMQDestination; 055import org.apache.activemq.command.ActiveMQMessage; 056import org.apache.activemq.command.ActiveMQTempDestination; 057import org.apache.activemq.command.ActiveMQTopic; 058import org.apache.activemq.command.BrokerId; 059import org.apache.activemq.command.BrokerInfo; 060import org.apache.activemq.command.Command; 061import org.apache.activemq.command.ConnectionError; 062import org.apache.activemq.command.ConnectionId; 063import org.apache.activemq.command.ConnectionInfo; 064import org.apache.activemq.command.ConsumerId; 065import org.apache.activemq.command.ConsumerInfo; 066import org.apache.activemq.command.DataStructure; 067import org.apache.activemq.command.DestinationInfo; 068import org.apache.activemq.command.ExceptionResponse; 069import org.apache.activemq.command.KeepAliveInfo; 070import org.apache.activemq.command.Message; 071import org.apache.activemq.command.MessageAck; 072import org.apache.activemq.command.MessageDispatch; 073import org.apache.activemq.command.MessageId; 074import org.apache.activemq.command.NetworkBridgeFilter; 075import org.apache.activemq.command.ProducerInfo; 076import org.apache.activemq.command.RemoveInfo; 077import org.apache.activemq.command.RemoveSubscriptionInfo; 078import org.apache.activemq.command.Response; 079import org.apache.activemq.command.SessionInfo; 080import org.apache.activemq.command.ShutdownInfo; 081import org.apache.activemq.command.SubscriptionInfo; 082import org.apache.activemq.command.WireFormatInfo; 083import org.apache.activemq.filter.DestinationFilter; 084import org.apache.activemq.filter.MessageEvaluationContext; 085import org.apache.activemq.security.SecurityContext; 086import org.apache.activemq.transport.DefaultTransportListener; 087import org.apache.activemq.transport.FutureResponse; 088import org.apache.activemq.transport.ResponseCallback; 089import org.apache.activemq.transport.Transport; 090import org.apache.activemq.transport.TransportDisposedIOException; 091import org.apache.activemq.transport.TransportFilter; 092import org.apache.activemq.transport.tcp.SslTransport; 093import org.apache.activemq.util.IdGenerator; 094import org.apache.activemq.util.IntrospectionSupport; 095import org.apache.activemq.util.LongSequenceGenerator; 096import org.apache.activemq.util.MarshallingSupport; 097import org.apache.activemq.util.ServiceStopper; 098import org.apache.activemq.util.ServiceSupport; 099import org.slf4j.Logger; 100import org.slf4j.LoggerFactory; 101 102/** 103 * A useful base class for implementing demand forwarding bridges. 104 */ 105public abstract class DemandForwardingBridgeSupport implements NetworkBridge, BrokerServiceAware { 106 private static final Logger LOG = LoggerFactory.getLogger(DemandForwardingBridgeSupport.class); 107 protected static final String DURABLE_SUB_PREFIX = "NC-DS_"; 108 protected final Transport localBroker; 109 protected final Transport remoteBroker; 110 protected IdGenerator idGenerator = new IdGenerator(); 111 protected final LongSequenceGenerator consumerIdGenerator = new LongSequenceGenerator(); 112 protected ConnectionInfo localConnectionInfo; 113 protected ConnectionInfo remoteConnectionInfo; 114 protected SessionInfo localSessionInfo; 115 protected ProducerInfo producerInfo; 116 protected String remoteBrokerName = "Unknown"; 117 protected String localClientId; 118 protected ConsumerInfo demandConsumerInfo; 119 protected int demandConsumerDispatched; 120 protected final AtomicBoolean localBridgeStarted = new AtomicBoolean(false); 121 protected final AtomicBoolean remoteBridgeStarted = new AtomicBoolean(false); 122 protected final AtomicBoolean bridgeFailed = new AtomicBoolean(); 123 protected final AtomicBoolean disposed = new AtomicBoolean(); 124 protected BrokerId localBrokerId; 125 protected ActiveMQDestination[] excludedDestinations; 126 protected ActiveMQDestination[] dynamicallyIncludedDestinations; 127 protected ActiveMQDestination[] staticallyIncludedDestinations; 128 protected ActiveMQDestination[] durableDestinations; 129 protected final ConcurrentHashMap<ConsumerId, DemandSubscription> subscriptionMapByLocalId = new ConcurrentHashMap<ConsumerId, DemandSubscription>(); 130 protected final ConcurrentHashMap<ConsumerId, DemandSubscription> subscriptionMapByRemoteId = new ConcurrentHashMap<ConsumerId, DemandSubscription>(); 131 protected final BrokerId localBrokerPath[] = new BrokerId[]{null}; 132 protected final CountDownLatch startedLatch = new CountDownLatch(2); 133 protected final CountDownLatch localStartedLatch = new CountDownLatch(1); 134 protected final AtomicBoolean lastConnectSucceeded = new AtomicBoolean(false); 135 protected NetworkBridgeConfiguration configuration; 136 protected final NetworkBridgeFilterFactory defaultFilterFactory = new DefaultNetworkBridgeFilterFactory(); 137 138 protected final BrokerId remoteBrokerPath[] = new BrokerId[]{null}; 139 protected BrokerId remoteBrokerId; 140 141 final AtomicLong enqueueCounter = new AtomicLong(); 142 final AtomicLong dequeueCounter = new AtomicLong(); 143 144 private NetworkBridgeListener networkBridgeListener; 145 private boolean createdByDuplex; 146 private BrokerInfo localBrokerInfo; 147 private BrokerInfo remoteBrokerInfo; 148 149 private final FutureBrokerInfo futureRemoteBrokerInfo = new FutureBrokerInfo(remoteBrokerInfo, disposed); 150 private final FutureBrokerInfo futureLocalBrokerInfo = new FutureBrokerInfo(localBrokerInfo, disposed); 151 152 private final AtomicBoolean started = new AtomicBoolean(); 153 private TransportConnection duplexInitiatingConnection; 154 private final AtomicBoolean duplexInitiatingConnectionInfoReceived = new AtomicBoolean(); 155 protected BrokerService brokerService = null; 156 private ObjectName mbeanObjectName; 157 private final ExecutorService serialExecutor = Executors.newSingleThreadExecutor(); 158 private Transport duplexInboundLocalBroker = null; 159 private ProducerInfo duplexInboundLocalProducerInfo; 160 161 public DemandForwardingBridgeSupport(NetworkBridgeConfiguration configuration, Transport localBroker, Transport remoteBroker) { 162 this.configuration = configuration; 163 this.localBroker = localBroker; 164 this.remoteBroker = remoteBroker; 165 } 166 167 public void duplexStart(TransportConnection connection, BrokerInfo localBrokerInfo, BrokerInfo remoteBrokerInfo) throws Exception { 168 this.localBrokerInfo = localBrokerInfo; 169 this.remoteBrokerInfo = remoteBrokerInfo; 170 this.duplexInitiatingConnection = connection; 171 start(); 172 serviceRemoteCommand(remoteBrokerInfo); 173 } 174 175 @Override 176 public void start() throws Exception { 177 if (started.compareAndSet(false, true)) { 178 179 if (brokerService == null) { 180 throw new IllegalArgumentException("BrokerService is null on " + this); 181 } 182 183 if (isDuplex()) { 184 duplexInboundLocalBroker = NetworkBridgeFactory.createLocalTransport(brokerService.getBroker()); 185 duplexInboundLocalBroker.setTransportListener(new DefaultTransportListener() { 186 187 @Override 188 public void onCommand(Object o) { 189 Command command = (Command) o; 190 serviceLocalCommand(command); 191 } 192 193 @Override 194 public void onException(IOException error) { 195 serviceLocalException(error); 196 } 197 }); 198 duplexInboundLocalBroker.start(); 199 } 200 201 localBroker.setTransportListener(new DefaultTransportListener() { 202 203 @Override 204 public void onCommand(Object o) { 205 Command command = (Command) o; 206 serviceLocalCommand(command); 207 } 208 209 @Override 210 public void onException(IOException error) { 211 if (!futureLocalBrokerInfo.isDone()) { 212 futureLocalBrokerInfo.cancel(true); 213 return; 214 } 215 serviceLocalException(error); 216 } 217 }); 218 219 remoteBroker.setTransportListener(new DefaultTransportListener() { 220 221 @Override 222 public void onCommand(Object o) { 223 Command command = (Command) o; 224 serviceRemoteCommand(command); 225 } 226 227 @Override 228 public void onException(IOException error) { 229 if (!futureRemoteBrokerInfo.isDone()) { 230 futureRemoteBrokerInfo.cancel(true); 231 return; 232 } 233 serviceRemoteException(error); 234 } 235 }); 236 237 remoteBroker.start(); 238 localBroker.start(); 239 240 if (!disposed.get()) { 241 try { 242 triggerStartAsyncNetworkBridgeCreation(); 243 } catch (IOException e) { 244 LOG.warn("Caught exception from remote start", e); 245 } 246 } else { 247 LOG.warn("Bridge was disposed before the start() method was fully executed."); 248 throw new TransportDisposedIOException(); 249 } 250 } 251 } 252 253 @Override 254 public void stop() throws Exception { 255 if (started.compareAndSet(true, false)) { 256 if (disposed.compareAndSet(false, true)) { 257 LOG.debug(" stopping {} bridge to {}", configuration.getBrokerName(), remoteBrokerName); 258 259 futureRemoteBrokerInfo.cancel(true); 260 futureLocalBrokerInfo.cancel(true); 261 262 NetworkBridgeListener l = this.networkBridgeListener; 263 if (l != null) { 264 l.onStop(this); 265 } 266 try { 267 // local start complete 268 if (startedLatch.getCount() < 2) { 269 LOG.trace("{} unregister bridge ({}) to {}", new Object[]{ 270 configuration.getBrokerName(), this, remoteBrokerName 271 }); 272 brokerService.getBroker().removeBroker(null, remoteBrokerInfo); 273 brokerService.getBroker().networkBridgeStopped(remoteBrokerInfo); 274 } 275 276 remoteBridgeStarted.set(false); 277 final CountDownLatch sendShutdown = new CountDownLatch(1); 278 279 brokerService.getTaskRunnerFactory().execute(new Runnable() { 280 @Override 281 public void run() { 282 try { 283 serialExecutor.shutdown(); 284 if (!serialExecutor.awaitTermination(5, TimeUnit.SECONDS)) { 285 List<Runnable> pendingTasks = serialExecutor.shutdownNow(); 286 LOG.info("pending tasks on stop {}", pendingTasks); 287 } 288 localBroker.oneway(new ShutdownInfo()); 289 remoteBroker.oneway(new ShutdownInfo()); 290 } catch (Throwable e) { 291 LOG.debug("Caught exception sending shutdown", e); 292 } finally { 293 sendShutdown.countDown(); 294 } 295 296 } 297 }, "ActiveMQ ForwardingBridge StopTask"); 298 299 if (!sendShutdown.await(10, TimeUnit.SECONDS)) { 300 LOG.info("Network Could not shutdown in a timely manner"); 301 } 302 } finally { 303 ServiceStopper ss = new ServiceStopper(); 304 ss.stop(remoteBroker); 305 ss.stop(localBroker); 306 ss.stop(duplexInboundLocalBroker); 307 // Release the started Latch since another thread could be 308 // stuck waiting for it to start up. 309 startedLatch.countDown(); 310 startedLatch.countDown(); 311 localStartedLatch.countDown(); 312 313 ss.throwFirstException(); 314 } 315 } 316 317 LOG.info("{} bridge to {} stopped", configuration.getBrokerName(), remoteBrokerName); 318 } 319 } 320 321 protected void triggerStartAsyncNetworkBridgeCreation() throws IOException { 322 brokerService.getTaskRunnerFactory().execute(new Runnable() { 323 @Override 324 public void run() { 325 final String originalName = Thread.currentThread().getName(); 326 Thread.currentThread().setName("triggerStartAsyncNetworkBridgeCreation: " + 327 "remoteBroker=" + remoteBroker + ", localBroker= " + localBroker); 328 329 try { 330 // First we collect the info data from both the local and remote ends 331 collectBrokerInfos(); 332 333 // Once we have all required broker info we can attempt to start 334 // the local and then remote sides of the bridge. 335 doStartLocalAndRemoteBridges(); 336 } finally { 337 Thread.currentThread().setName(originalName); 338 } 339 } 340 }); 341 } 342 343 private void collectBrokerInfos() { 344 345 // First wait for the remote to feed us its BrokerInfo, then we can check on 346 // the LocalBrokerInfo and decide is this is a loop. 347 try { 348 remoteBrokerInfo = futureRemoteBrokerInfo.get(); 349 if (remoteBrokerInfo == null) { 350 fireBridgeFailed(new Throwable("remoteBrokerInfo is null")); 351 return; 352 } 353 } catch (Exception e) { 354 serviceRemoteException(e); 355 return; 356 } 357 358 try { 359 localBrokerInfo = futureLocalBrokerInfo.get(); 360 if (localBrokerInfo == null) { 361 fireBridgeFailed(new Throwable("localBrokerInfo is null")); 362 return; 363 } 364 365 // Before we try and build the bridge lets check if we are in a loop 366 // and if so just stop now before registering anything. 367 remoteBrokerId = remoteBrokerInfo.getBrokerId(); 368 if (localBrokerId.equals(remoteBrokerId)) { 369 LOG.trace("{} disconnecting remote loop back connector for: {}, with id: {}", new Object[]{ 370 configuration.getBrokerName(), remoteBrokerName, remoteBrokerId 371 }); 372 ServiceSupport.dispose(localBroker); 373 ServiceSupport.dispose(remoteBroker); 374 // the bridge is left in a bit of limbo, but it won't get retried 375 // in this state. 376 return; 377 } 378 379 // Fill in the remote broker's information now. 380 remoteBrokerPath[0] = remoteBrokerId; 381 remoteBrokerName = remoteBrokerInfo.getBrokerName(); 382 if (configuration.isUseBrokerNamesAsIdSeed()) { 383 idGenerator = new IdGenerator(brokerService.getBrokerName() + "->" + remoteBrokerName); 384 } 385 } catch (Throwable e) { 386 serviceLocalException(e); 387 } 388 } 389 390 private void doStartLocalAndRemoteBridges() { 391 392 if (disposed.get()) { 393 return; 394 } 395 396 if (isCreatedByDuplex()) { 397 // apply remote (propagated) configuration to local duplex bridge before start 398 Properties props = null; 399 try { 400 props = MarshallingSupport.stringToProperties(remoteBrokerInfo.getNetworkProperties()); 401 IntrospectionSupport.getProperties(configuration, props, null); 402 if (configuration.getExcludedDestinations() != null) { 403 excludedDestinations = configuration.getExcludedDestinations().toArray( 404 new ActiveMQDestination[configuration.getExcludedDestinations().size()]); 405 } 406 if (configuration.getStaticallyIncludedDestinations() != null) { 407 staticallyIncludedDestinations = configuration.getStaticallyIncludedDestinations().toArray( 408 new ActiveMQDestination[configuration.getStaticallyIncludedDestinations().size()]); 409 } 410 if (configuration.getDynamicallyIncludedDestinations() != null) { 411 dynamicallyIncludedDestinations = configuration.getDynamicallyIncludedDestinations().toArray( 412 new ActiveMQDestination[configuration.getDynamicallyIncludedDestinations().size()]); 413 } 414 } catch (Throwable t) { 415 LOG.error("Error mapping remote configuration: {}", props, t); 416 } 417 } 418 419 try { 420 startLocalBridge(); 421 } catch (Throwable e) { 422 serviceLocalException(e); 423 return; 424 } 425 426 try { 427 startRemoteBridge(); 428 } catch (Throwable e) { 429 serviceRemoteException(e); 430 return; 431 } 432 433 try { 434 if (safeWaitUntilStarted()) { 435 setupStaticDestinations(); 436 } 437 } catch (Throwable e) { 438 serviceLocalException(e); 439 } 440 } 441 442 private void startLocalBridge() throws Throwable { 443 if (!bridgeFailed.get() && localBridgeStarted.compareAndSet(false, true)) { 444 synchronized (this) { 445 LOG.trace("{} starting local Bridge, localBroker={}", configuration.getBrokerName(), localBroker); 446 if (!disposed.get()) { 447 448 if (idGenerator == null) { 449 throw new IllegalStateException("Id Generator cannot be null"); 450 } 451 452 localConnectionInfo = new ConnectionInfo(); 453 localConnectionInfo.setConnectionId(new ConnectionId(idGenerator.generateId())); 454 localClientId = configuration.getName() + "_" + remoteBrokerName + "_inbound_" + configuration.getBrokerName(); 455 localConnectionInfo.setClientId(localClientId); 456 localConnectionInfo.setUserName(configuration.getUserName()); 457 localConnectionInfo.setPassword(configuration.getPassword()); 458 Transport originalTransport = remoteBroker; 459 while (originalTransport instanceof TransportFilter) { 460 originalTransport = ((TransportFilter) originalTransport).getNext(); 461 } 462 if (originalTransport instanceof SslTransport) { 463 X509Certificate[] peerCerts = ((SslTransport) originalTransport).getPeerCertificates(); 464 localConnectionInfo.setTransportContext(peerCerts); 465 } 466 // sync requests that may fail 467 Object resp = localBroker.request(localConnectionInfo); 468 if (resp instanceof ExceptionResponse) { 469 throw ((ExceptionResponse) resp).getException(); 470 } 471 localSessionInfo = new SessionInfo(localConnectionInfo, 1); 472 localBroker.oneway(localSessionInfo); 473 474 if (configuration.isDuplex()) { 475 // separate in-bound channel for forwards so we don't 476 // contend with out-bound dispatch on same connection 477 ConnectionInfo duplexLocalConnectionInfo = new ConnectionInfo(); 478 duplexLocalConnectionInfo.setConnectionId(new ConnectionId(idGenerator.generateId())); 479 duplexLocalConnectionInfo.setClientId(configuration.getName() + "_" + remoteBrokerName + "_inbound_duplex_" 480 + configuration.getBrokerName()); 481 duplexLocalConnectionInfo.setUserName(configuration.getUserName()); 482 duplexLocalConnectionInfo.setPassword(configuration.getPassword()); 483 484 if (originalTransport instanceof SslTransport) { 485 X509Certificate[] peerCerts = ((SslTransport) originalTransport).getPeerCertificates(); 486 duplexLocalConnectionInfo.setTransportContext(peerCerts); 487 } 488 // sync requests that may fail 489 resp = duplexInboundLocalBroker.request(duplexLocalConnectionInfo); 490 if (resp instanceof ExceptionResponse) { 491 throw ((ExceptionResponse) resp).getException(); 492 } 493 SessionInfo duplexInboundSession = new SessionInfo(duplexLocalConnectionInfo, 1); 494 duplexInboundLocalProducerInfo = new ProducerInfo(duplexInboundSession, 1); 495 duplexInboundLocalBroker.oneway(duplexInboundSession); 496 duplexInboundLocalBroker.oneway(duplexInboundLocalProducerInfo); 497 } 498 brokerService.getBroker().networkBridgeStarted(remoteBrokerInfo, this.createdByDuplex, remoteBroker.toString()); 499 NetworkBridgeListener l = this.networkBridgeListener; 500 if (l != null) { 501 l.onStart(this); 502 } 503 504 // Let the local broker know the remote broker's ID. 505 localBroker.oneway(remoteBrokerInfo); 506 // new peer broker (a consumer can work with remote broker also) 507 brokerService.getBroker().addBroker(null, remoteBrokerInfo); 508 509 LOG.info("Network connection between {} and {} ({}) has been established.", new Object[]{ 510 localBroker, remoteBroker, remoteBrokerName 511 }); 512 LOG.trace("{} register bridge ({}) to {}", new Object[]{ 513 configuration.getBrokerName(), this, remoteBrokerName 514 }); 515 } else { 516 LOG.warn("Bridge was disposed before the startLocalBridge() method was fully executed."); 517 } 518 startedLatch.countDown(); 519 localStartedLatch.countDown(); 520 } 521 } 522 } 523 524 protected void startRemoteBridge() throws Exception { 525 if (!bridgeFailed.get() && remoteBridgeStarted.compareAndSet(false, true)) { 526 LOG.trace("{} starting remote Bridge, remoteBroker={}", configuration.getBrokerName(), remoteBroker); 527 synchronized (this) { 528 if (!isCreatedByDuplex()) { 529 BrokerInfo brokerInfo = new BrokerInfo(); 530 brokerInfo.setBrokerName(configuration.getBrokerName()); 531 brokerInfo.setBrokerURL(configuration.getBrokerURL()); 532 brokerInfo.setNetworkConnection(true); 533 brokerInfo.setDuplexConnection(configuration.isDuplex()); 534 // set our properties 535 Properties props = new Properties(); 536 IntrospectionSupport.getProperties(configuration, props, null); 537 props.remove("networkTTL"); 538 String str = MarshallingSupport.propertiesToString(props); 539 brokerInfo.setNetworkProperties(str); 540 brokerInfo.setBrokerId(this.localBrokerId); 541 remoteBroker.oneway(brokerInfo); 542 } 543 if (remoteConnectionInfo != null) { 544 remoteBroker.oneway(remoteConnectionInfo.createRemoveCommand()); 545 } 546 remoteConnectionInfo = new ConnectionInfo(); 547 remoteConnectionInfo.setConnectionId(new ConnectionId(idGenerator.generateId())); 548 remoteConnectionInfo.setClientId(configuration.getName() + "_" + configuration.getBrokerName() + "_outbound"); 549 remoteConnectionInfo.setUserName(configuration.getUserName()); 550 remoteConnectionInfo.setPassword(configuration.getPassword()); 551 remoteBroker.oneway(remoteConnectionInfo); 552 553 SessionInfo remoteSessionInfo = new SessionInfo(remoteConnectionInfo, 1); 554 remoteBroker.oneway(remoteSessionInfo); 555 producerInfo = new ProducerInfo(remoteSessionInfo, 1); 556 producerInfo.setResponseRequired(false); 557 remoteBroker.oneway(producerInfo); 558 // Listen to consumer advisory messages on the remote broker to determine demand. 559 if (!configuration.isStaticBridge()) { 560 demandConsumerInfo = new ConsumerInfo(remoteSessionInfo, 1); 561 // always dispatch advisory message asynchronously so that 562 // we never block the producer broker if we are slow 563 demandConsumerInfo.setDispatchAsync(true); 564 String advisoryTopic = configuration.getDestinationFilter(); 565 if (configuration.isBridgeTempDestinations()) { 566 advisoryTopic += "," + AdvisorySupport.TEMP_DESTINATION_COMPOSITE_ADVISORY_TOPIC; 567 } 568 demandConsumerInfo.setDestination(new ActiveMQTopic(advisoryTopic)); 569 demandConsumerInfo.setPrefetchSize(configuration.getPrefetchSize()); 570 remoteBroker.oneway(demandConsumerInfo); 571 } 572 startedLatch.countDown(); 573 } 574 } 575 } 576 577 @Override 578 public void serviceRemoteException(Throwable error) { 579 if (!disposed.get()) { 580 if (error instanceof SecurityException || error instanceof GeneralSecurityException) { 581 LOG.error("Network connection between {} and {} shutdown due to a remote error: {}", new Object[]{ 582 localBroker, remoteBroker, error 583 }); 584 } else { 585 LOG.warn("Network connection between {} and {} shutdown due to a remote error: {}", new Object[]{ 586 localBroker, remoteBroker, error 587 }); 588 } 589 LOG.debug("The remote Exception was: {}", error, error); 590 brokerService.getTaskRunnerFactory().execute(new Runnable() { 591 @Override 592 public void run() { 593 ServiceSupport.dispose(getControllingService()); 594 } 595 }); 596 fireBridgeFailed(error); 597 } 598 } 599 600 protected void serviceRemoteCommand(Command command) { 601 if (!disposed.get()) { 602 try { 603 if (command.isMessageDispatch()) { 604 safeWaitUntilStarted(); 605 MessageDispatch md = (MessageDispatch) command; 606 serviceRemoteConsumerAdvisory(md.getMessage().getDataStructure()); 607 ackAdvisory(md.getMessage()); 608 } else if (command.isBrokerInfo()) { 609 futureRemoteBrokerInfo.set((BrokerInfo) command); 610 } else if (command.getClass() == ConnectionError.class) { 611 ConnectionError ce = (ConnectionError) command; 612 serviceRemoteException(ce.getException()); 613 } else { 614 if (isDuplex()) { 615 LOG.trace("{} duplex command type: {}", configuration.getBrokerName(), command.getDataStructureType()); 616 if (command.isMessage()) { 617 final ActiveMQMessage message = (ActiveMQMessage) command; 618 if (AdvisorySupport.isConsumerAdvisoryTopic(message.getDestination()) 619 || AdvisorySupport.isDestinationAdvisoryTopic(message.getDestination())) { 620 serviceRemoteConsumerAdvisory(message.getDataStructure()); 621 ackAdvisory(message); 622 } else { 623 if (!isPermissableDestination(message.getDestination(), true)) { 624 return; 625 } 626 // message being forwarded - we need to 627 // propagate the response to our local send 628 if (canDuplexDispatch(message)) { 629 message.setProducerId(duplexInboundLocalProducerInfo.getProducerId()); 630 if (message.isResponseRequired() || configuration.isAlwaysSyncSend()) { 631 duplexInboundLocalBroker.asyncRequest(message, new ResponseCallback() { 632 final int correlationId = message.getCommandId(); 633 634 @Override 635 public void onCompletion(FutureResponse resp) { 636 try { 637 Response reply = resp.getResult(); 638 reply.setCorrelationId(correlationId); 639 remoteBroker.oneway(reply); 640 } catch (IOException error) { 641 LOG.error("Exception: {} on duplex forward of: {}", error, message); 642 serviceRemoteException(error); 643 } 644 } 645 }); 646 } else { 647 duplexInboundLocalBroker.oneway(message); 648 } 649 serviceInboundMessage(message); 650 } else { 651 if (message.isResponseRequired() || configuration.isAlwaysSyncSend()) { 652 Response reply = new Response(); 653 reply.setCorrelationId(message.getCommandId()); 654 remoteBroker.oneway(reply); 655 } 656 } 657 } 658 } else { 659 switch (command.getDataStructureType()) { 660 case ConnectionInfo.DATA_STRUCTURE_TYPE: 661 if (duplexInitiatingConnection != null && duplexInitiatingConnectionInfoReceived.compareAndSet(false, true)) { 662 // end of initiating connection setup - propogate to initial connection to get mbean by clientid 663 duplexInitiatingConnection.processAddConnection((ConnectionInfo) command); 664 } else { 665 localBroker.oneway(command); 666 } 667 break; 668 case SessionInfo.DATA_STRUCTURE_TYPE: 669 localBroker.oneway(command); 670 break; 671 case ProducerInfo.DATA_STRUCTURE_TYPE: 672 // using duplexInboundLocalProducerInfo 673 break; 674 case MessageAck.DATA_STRUCTURE_TYPE: 675 MessageAck ack = (MessageAck) command; 676 DemandSubscription localSub = subscriptionMapByRemoteId.get(ack.getConsumerId()); 677 if (localSub != null) { 678 ack.setConsumerId(localSub.getLocalInfo().getConsumerId()); 679 localBroker.oneway(ack); 680 } else { 681 LOG.warn("Matching local subscription not found for ack: {}", ack); 682 } 683 break; 684 case ConsumerInfo.DATA_STRUCTURE_TYPE: 685 localStartedLatch.await(); 686 if (started.get()) { 687 addConsumerInfo((ConsumerInfo) command); 688 } else { 689 // received a subscription whilst stopping 690 LOG.warn("Stopping - ignoring ConsumerInfo: {}", command); 691 } 692 break; 693 case ShutdownInfo.DATA_STRUCTURE_TYPE: 694 // initiator is shutting down, controlled case 695 // abortive close dealt with by inactivity monitor 696 LOG.info("Stopping network bridge on shutdown of remote broker"); 697 serviceRemoteException(new IOException(command.toString())); 698 break; 699 default: 700 LOG.debug("Ignoring remote command: {}", command); 701 } 702 } 703 } else { 704 switch (command.getDataStructureType()) { 705 case KeepAliveInfo.DATA_STRUCTURE_TYPE: 706 case WireFormatInfo.DATA_STRUCTURE_TYPE: 707 case ShutdownInfo.DATA_STRUCTURE_TYPE: 708 break; 709 default: 710 LOG.warn("Unexpected remote command: {}", command); 711 } 712 } 713 } 714 } catch (Throwable e) { 715 LOG.debug("Exception processing remote command: {}", command, e); 716 serviceRemoteException(e); 717 } 718 } 719 } 720 721 private void ackAdvisory(Message message) throws IOException { 722 demandConsumerDispatched++; 723 if (demandConsumerDispatched > (demandConsumerInfo.getPrefetchSize() * .75)) { 724 MessageAck ack = new MessageAck(message, MessageAck.STANDARD_ACK_TYPE, demandConsumerDispatched); 725 ack.setConsumerId(demandConsumerInfo.getConsumerId()); 726 remoteBroker.oneway(ack); 727 demandConsumerDispatched = 0; 728 } 729 } 730 731 private void serviceRemoteConsumerAdvisory(DataStructure data) throws IOException { 732 final int networkTTL = configuration.getConsumerTTL(); 733 if (data.getClass() == ConsumerInfo.class) { 734 // Create a new local subscription 735 ConsumerInfo info = (ConsumerInfo) data; 736 BrokerId[] path = info.getBrokerPath(); 737 738 if (info.isBrowser()) { 739 LOG.debug("{} Ignoring sub from {}, browsers explicitly suppressed", configuration.getBrokerName(), remoteBrokerName); 740 return; 741 } 742 743 if (path != null && networkTTL > -1 && path.length >= networkTTL) { 744 LOG.debug("{} Ignoring sub from {}, restricted to {} network hops only: {}", new Object[]{ 745 configuration.getBrokerName(), remoteBrokerName, networkTTL, info 746 }); 747 return; 748 } 749 750 if (contains(path, localBrokerPath[0])) { 751 // Ignore this consumer as it's a consumer we locally sent to the broker. 752 LOG.debug("{} Ignoring sub from {}, already routed through this broker once: {}", new Object[]{ 753 configuration.getBrokerName(), remoteBrokerName, info 754 }); 755 return; 756 } 757 758 if (!isPermissableDestination(info.getDestination())) { 759 // ignore if not in the permitted or in the excluded list 760 LOG.debug("{} Ignoring sub from {}, destination {} is not permitted: {}", new Object[]{ 761 configuration.getBrokerName(), remoteBrokerName, info.getDestination(), info 762 }); 763 return; 764 } 765 766 // in a cyclic network there can be multiple bridges per broker that can propagate 767 // a network subscription so there is a need to synchronize on a shared entity 768 synchronized (brokerService.getVmConnectorURI()) { 769 addConsumerInfo(info); 770 } 771 } else if (data.getClass() == DestinationInfo.class) { 772 // It's a destination info - we want to pass up information about temporary destinations 773 final DestinationInfo destInfo = (DestinationInfo) data; 774 BrokerId[] path = destInfo.getBrokerPath(); 775 if (path != null && networkTTL > -1 && path.length >= networkTTL) { 776 LOG.debug("{} Ignoring destination {} restricted to {} network hops only", new Object[]{ 777 configuration.getBrokerName(), destInfo, networkTTL 778 }); 779 return; 780 } 781 if (contains(destInfo.getBrokerPath(), localBrokerPath[0])) { 782 LOG.debug("{} Ignoring destination {} already routed through this broker once", configuration.getBrokerName(), destInfo); 783 return; 784 } 785 destInfo.setConnectionId(localConnectionInfo.getConnectionId()); 786 if (destInfo.getDestination() instanceof ActiveMQTempDestination) { 787 // re-set connection id so comes from here 788 ActiveMQTempDestination tempDest = (ActiveMQTempDestination) destInfo.getDestination(); 789 tempDest.setConnectionId(localSessionInfo.getSessionId().getConnectionId()); 790 } 791 destInfo.setBrokerPath(appendToBrokerPath(destInfo.getBrokerPath(), getRemoteBrokerPath())); 792 LOG.trace("{} bridging {} destination on {} from {}, destination: {}", new Object[]{ 793 configuration.getBrokerName(), (destInfo.isAddOperation() ? "add" : "remove"), localBroker, remoteBrokerName, destInfo 794 }); 795 if (destInfo.isRemoveOperation()) { 796 // Serialize with removeSub operations such that all removeSub advisories 797 // are generated 798 serialExecutor.execute(new Runnable() { 799 @Override 800 public void run() { 801 try { 802 localBroker.oneway(destInfo); 803 } catch (IOException e) { 804 LOG.warn("failed to deliver remove command for destination: {}", destInfo.getDestination(), e); 805 } 806 } 807 }); 808 } else { 809 localBroker.oneway(destInfo); 810 } 811 } else if (data.getClass() == RemoveInfo.class) { 812 ConsumerId id = (ConsumerId) ((RemoveInfo) data).getObjectId(); 813 removeDemandSubscription(id); 814 } else if (data.getClass() == RemoveSubscriptionInfo.class) { 815 RemoveSubscriptionInfo info = ((RemoveSubscriptionInfo) data); 816 SubscriptionInfo subscriptionInfo = new SubscriptionInfo(info.getClientId(), info.getSubscriptionName()); 817 for (Iterator<DemandSubscription> i = subscriptionMapByLocalId.values().iterator(); i.hasNext(); ) { 818 DemandSubscription ds = i.next(); 819 boolean removed = ds.getDurableRemoteSubs().remove(subscriptionInfo); 820 if (removed) { 821 if (ds.getDurableRemoteSubs().isEmpty()) { 822 823 // deactivate subscriber 824 RemoveInfo removeInfo = new RemoveInfo(ds.getLocalInfo().getConsumerId()); 825 localBroker.oneway(removeInfo); 826 827 // remove subscriber 828 RemoveSubscriptionInfo sending = new RemoveSubscriptionInfo(); 829 sending.setClientId(localClientId); 830 sending.setSubscriptionName(ds.getLocalDurableSubscriber().getSubscriptionName()); 831 sending.setConnectionId(this.localConnectionInfo.getConnectionId()); 832 localBroker.oneway(sending); 833 } 834 } 835 } 836 } 837 } 838 839 @Override 840 public void serviceLocalException(Throwable error) { 841 serviceLocalException(null, error); 842 } 843 844 public void serviceLocalException(MessageDispatch messageDispatch, Throwable error) { 845 LOG.trace("serviceLocalException: disposed {} ex", disposed.get(), error); 846 if (!disposed.get()) { 847 if (error instanceof DestinationDoesNotExistException && ((DestinationDoesNotExistException) error).isTemporary()) { 848 // not a reason to terminate the bridge - temps can disappear with 849 // pending sends as the demand sub may outlive the remote dest 850 if (messageDispatch != null) { 851 LOG.warn("PoisonAck of {} on forwarding error: {}", messageDispatch.getMessage().getMessageId(), error); 852 try { 853 MessageAck poisonAck = new MessageAck(messageDispatch, MessageAck.POSION_ACK_TYPE, 1); 854 poisonAck.setPoisonCause(error); 855 localBroker.oneway(poisonAck); 856 } catch (IOException ioe) { 857 LOG.error("Failed to posion ack message following forward failure: ", ioe); 858 } 859 fireFailedForwardAdvisory(messageDispatch, error); 860 } else { 861 LOG.warn("Ignoring exception on forwarding to non existent temp dest: ", error); 862 } 863 return; 864 } 865 866 LOG.info("Network connection between {} and {} shutdown due to a local error: {}", new Object[]{localBroker, remoteBroker, error}); 867 LOG.debug("The local Exception was: {}", error, error); 868 869 brokerService.getTaskRunnerFactory().execute(new Runnable() { 870 @Override 871 public void run() { 872 ServiceSupport.dispose(getControllingService()); 873 } 874 }); 875 fireBridgeFailed(error); 876 } 877 } 878 879 private void fireFailedForwardAdvisory(MessageDispatch messageDispatch, Throwable error) { 880 if (configuration.isAdvisoryForFailedForward()) { 881 AdvisoryBroker advisoryBroker = null; 882 try { 883 advisoryBroker = (AdvisoryBroker) brokerService.getBroker().getAdaptor(AdvisoryBroker.class); 884 885 if (advisoryBroker != null) { 886 ConnectionContext context = new ConnectionContext(); 887 context.setSecurityContext(SecurityContext.BROKER_SECURITY_CONTEXT); 888 context.setBroker(brokerService.getBroker()); 889 890 ActiveMQMessage advisoryMessage = new ActiveMQMessage(); 891 advisoryMessage.setStringProperty("cause", error.getLocalizedMessage()); 892 advisoryBroker.fireAdvisory(context, AdvisorySupport.getNetworkBridgeForwardFailureAdvisoryTopic(), messageDispatch.getMessage(), null, 893 advisoryMessage); 894 895 } 896 } catch (Exception e) { 897 LOG.warn("failed to fire forward failure advisory, cause: {}", e); 898 LOG.debug("detail", e); 899 } 900 } 901 } 902 903 protected Service getControllingService() { 904 return duplexInitiatingConnection != null ? duplexInitiatingConnection : DemandForwardingBridgeSupport.this; 905 } 906 907 protected void addSubscription(DemandSubscription sub) throws IOException { 908 if (sub != null) { 909 if (isDuplex()) { 910 // async vm transport, need to wait for completion 911 localBroker.request(sub.getLocalInfo()); 912 } else { 913 localBroker.oneway(sub.getLocalInfo()); 914 } 915 } 916 } 917 918 protected void removeSubscription(final DemandSubscription sub) throws IOException { 919 if (sub != null) { 920 LOG.trace("{} remove local subscription: {} for remote {}", new Object[]{configuration.getBrokerName(), sub.getLocalInfo().getConsumerId(), sub.getRemoteInfo().getConsumerId()}); 921 922 // ensure not available for conduit subs pending removal 923 subscriptionMapByLocalId.remove(sub.getLocalInfo().getConsumerId()); 924 subscriptionMapByRemoteId.remove(sub.getRemoteInfo().getConsumerId()); 925 926 // continue removal in separate thread to free up this thread for outstanding responses 927 // Serialize with removeDestination operations so that removeSubs are serialized with 928 // removeDestinations such that all removeSub advisories are generated 929 serialExecutor.execute(new Runnable() { 930 @Override 931 public void run() { 932 sub.waitForCompletion(); 933 try { 934 localBroker.oneway(sub.getLocalInfo().createRemoveCommand()); 935 } catch (IOException e) { 936 LOG.warn("failed to deliver remove command for local subscription, for remote {}", sub.getRemoteInfo().getConsumerId(), e); 937 } 938 } 939 }); 940 } 941 } 942 943 protected Message configureMessage(MessageDispatch md) throws IOException { 944 Message message = md.getMessage().copy(); 945 // Update the packet to show where it came from. 946 message.setBrokerPath(appendToBrokerPath(message.getBrokerPath(), localBrokerPath)); 947 message.setProducerId(producerInfo.getProducerId()); 948 message.setDestination(md.getDestination()); 949 message.setMemoryUsage(null); 950 if (message.getOriginalTransactionId() == null) { 951 message.setOriginalTransactionId(message.getTransactionId()); 952 } 953 message.setTransactionId(null); 954 if (configuration.isUseCompression()) { 955 message.compress(); 956 } 957 return message; 958 } 959 960 protected void serviceLocalCommand(Command command) { 961 if (!disposed.get()) { 962 try { 963 if (command.isMessageDispatch()) { 964 safeWaitUntilStarted(); 965 enqueueCounter.incrementAndGet(); 966 final MessageDispatch md = (MessageDispatch) command; 967 final DemandSubscription sub = subscriptionMapByLocalId.get(md.getConsumerId()); 968 if (sub != null && md.getMessage() != null && sub.incrementOutstandingResponses()) { 969 970 if (suppressMessageDispatch(md, sub)) { 971 LOG.debug("{} message not forwarded to {} because message came from there or fails TTL, brokerPath: {}, message: {}", new Object[]{ 972 configuration.getBrokerName(), remoteBrokerName, Arrays.toString(md.getMessage().getBrokerPath()), md.getMessage() 973 }); 974 // still ack as it may be durable 975 try { 976 localBroker.oneway(new MessageAck(md, MessageAck.INDIVIDUAL_ACK_TYPE, 1)); 977 } finally { 978 sub.decrementOutstandingResponses(); 979 } 980 return; 981 } 982 983 Message message = configureMessage(md); 984 LOG.debug("bridging ({} -> {}), consumer: {}, destinaition: {}, brokerPath: {}, message: {}", new Object[]{ 985 configuration.getBrokerName(), remoteBrokerName, (LOG.isTraceEnabled() ? message : message.getMessageId()), md.getConsumerId(), message.getDestination(), Arrays.toString(message.getBrokerPath()), message 986 }); 987 988 if (isDuplex() && AdvisorySupport.ADIVSORY_MESSAGE_TYPE.equals(message.getType())) { 989 try { 990 // never request b/c they are eventually acked async 991 remoteBroker.oneway(message); 992 } finally { 993 sub.decrementOutstandingResponses(); 994 } 995 return; 996 } 997 998 if (message.isPersistent() || configuration.isAlwaysSyncSend()) { 999 1000 // The message was not sent using async send, so we should only 1001 // ack the local broker when we get confirmation that the remote 1002 // broker has received the message. 1003 remoteBroker.asyncRequest(message, new ResponseCallback() { 1004 @Override 1005 public void onCompletion(FutureResponse future) { 1006 try { 1007 Response response = future.getResult(); 1008 if (response.isException()) { 1009 ExceptionResponse er = (ExceptionResponse) response; 1010 serviceLocalException(md, er.getException()); 1011 } else { 1012 localBroker.oneway(new MessageAck(md, MessageAck.INDIVIDUAL_ACK_TYPE, 1)); 1013 dequeueCounter.incrementAndGet(); 1014 } 1015 } catch (IOException e) { 1016 serviceLocalException(md, e); 1017 } finally { 1018 sub.decrementOutstandingResponses(); 1019 } 1020 } 1021 }); 1022 1023 } else { 1024 // If the message was originally sent using async send, we will 1025 // preserve that QOS by bridging it using an async send (small chance 1026 // of message loss). 1027 try { 1028 remoteBroker.oneway(message); 1029 localBroker.oneway(new MessageAck(md, MessageAck.INDIVIDUAL_ACK_TYPE, 1)); 1030 dequeueCounter.incrementAndGet(); 1031 } finally { 1032 sub.decrementOutstandingResponses(); 1033 } 1034 } 1035 serviceOutbound(message); 1036 } else { 1037 LOG.debug("No subscription registered with this network bridge for consumerId: {} for message: {}", md.getConsumerId(), md.getMessage()); 1038 } 1039 } else if (command.isBrokerInfo()) { 1040 futureLocalBrokerInfo.set((BrokerInfo) command); 1041 } else if (command.isShutdownInfo()) { 1042 LOG.info("{} Shutting down", configuration.getBrokerName()); 1043 stop(); 1044 } else if (command.getClass() == ConnectionError.class) { 1045 ConnectionError ce = (ConnectionError) command; 1046 serviceLocalException(ce.getException()); 1047 } else { 1048 switch (command.getDataStructureType()) { 1049 case WireFormatInfo.DATA_STRUCTURE_TYPE: 1050 break; 1051 default: 1052 LOG.warn("Unexpected local command: {}", command); 1053 } 1054 } 1055 } catch (Throwable e) { 1056 LOG.warn("Caught an exception processing local command", e); 1057 serviceLocalException(e); 1058 } 1059 } 1060 } 1061 1062 private boolean suppressMessageDispatch(MessageDispatch md, DemandSubscription sub) throws Exception { 1063 boolean suppress = false; 1064 // for durable subs, suppression via filter leaves dangling acks so we 1065 // need to check here and allow the ack irrespective 1066 if (sub.getLocalInfo().isDurable()) { 1067 MessageEvaluationContext messageEvalContext = new MessageEvaluationContext(); 1068 messageEvalContext.setMessageReference(md.getMessage()); 1069 messageEvalContext.setDestination(md.getDestination()); 1070 suppress = !sub.getNetworkBridgeFilter().matches(messageEvalContext); 1071 } 1072 return suppress; 1073 } 1074 1075 public static boolean contains(BrokerId[] brokerPath, BrokerId brokerId) { 1076 if (brokerPath != null) { 1077 for (BrokerId id : brokerPath) { 1078 if (brokerId.equals(id)) { 1079 return true; 1080 } 1081 } 1082 } 1083 return false; 1084 } 1085 1086 protected BrokerId[] appendToBrokerPath(BrokerId[] brokerPath, BrokerId[] pathsToAppend) { 1087 if (brokerPath == null || brokerPath.length == 0) { 1088 return pathsToAppend; 1089 } 1090 BrokerId rc[] = new BrokerId[brokerPath.length + pathsToAppend.length]; 1091 System.arraycopy(brokerPath, 0, rc, 0, brokerPath.length); 1092 System.arraycopy(pathsToAppend, 0, rc, brokerPath.length, pathsToAppend.length); 1093 return rc; 1094 } 1095 1096 protected BrokerId[] appendToBrokerPath(BrokerId[] brokerPath, BrokerId idToAppend) { 1097 if (brokerPath == null || brokerPath.length == 0) { 1098 return new BrokerId[]{idToAppend}; 1099 } 1100 BrokerId rc[] = new BrokerId[brokerPath.length + 1]; 1101 System.arraycopy(brokerPath, 0, rc, 0, brokerPath.length); 1102 rc[brokerPath.length] = idToAppend; 1103 return rc; 1104 } 1105 1106 protected boolean isPermissableDestination(ActiveMQDestination destination) { 1107 return isPermissableDestination(destination, false); 1108 } 1109 1110 protected boolean isPermissableDestination(ActiveMQDestination destination, boolean allowTemporary) { 1111 // Are we not bridging temporary destinations? 1112 if (destination.isTemporary()) { 1113 if (allowTemporary) { 1114 return true; 1115 } else { 1116 return configuration.isBridgeTempDestinations(); 1117 } 1118 } 1119 1120 ActiveMQDestination[] dests = staticallyIncludedDestinations; 1121 if (dests != null && dests.length > 0) { 1122 for (ActiveMQDestination dest : dests) { 1123 DestinationFilter inclusionFilter = DestinationFilter.parseFilter(dest); 1124 if (dest != null && inclusionFilter.matches(destination) && dest.getDestinationType() == destination.getDestinationType()) { 1125 return true; 1126 } 1127 } 1128 } 1129 1130 dests = excludedDestinations; 1131 if (dests != null && dests.length > 0) { 1132 for (ActiveMQDestination dest : dests) { 1133 DestinationFilter exclusionFilter = DestinationFilter.parseFilter(dest); 1134 if (dest != null && exclusionFilter.matches(destination) && dest.getDestinationType() == destination.getDestinationType()) { 1135 return false; 1136 } 1137 } 1138 } 1139 1140 dests = dynamicallyIncludedDestinations; 1141 if (dests != null && dests.length > 0) { 1142 for (ActiveMQDestination dest : dests) { 1143 DestinationFilter inclusionFilter = DestinationFilter.parseFilter(dest); 1144 if (dest != null && inclusionFilter.matches(destination) && dest.getDestinationType() == destination.getDestinationType()) { 1145 return true; 1146 } 1147 } 1148 1149 return false; 1150 } 1151 return true; 1152 } 1153 1154 /** 1155 * Subscriptions for these destinations are always created 1156 */ 1157 protected void setupStaticDestinations() { 1158 ActiveMQDestination[] dests = staticallyIncludedDestinations; 1159 if (dests != null) { 1160 for (ActiveMQDestination dest : dests) { 1161 DemandSubscription sub = createDemandSubscription(dest); 1162 sub.setStaticallyIncluded(true); 1163 try { 1164 addSubscription(sub); 1165 } catch (IOException e) { 1166 LOG.error("Failed to add static destination {}", dest, e); 1167 } 1168 LOG.trace("{}, bridging messages for static destination: {}", configuration.getBrokerName(), dest); 1169 } 1170 } 1171 } 1172 1173 protected void addConsumerInfo(final ConsumerInfo consumerInfo) throws IOException { 1174 ConsumerInfo info = consumerInfo.copy(); 1175 addRemoteBrokerToBrokerPath(info); 1176 DemandSubscription sub = createDemandSubscription(info); 1177 if (sub != null) { 1178 if (duplicateSuppressionIsRequired(sub)) { 1179 undoMapRegistration(sub); 1180 } else { 1181 if (consumerInfo.isDurable()) { 1182 sub.getDurableRemoteSubs().add(new SubscriptionInfo(sub.getRemoteInfo().getClientId(), consumerInfo.getSubscriptionName())); 1183 } 1184 addSubscription(sub); 1185 LOG.debug("{} new demand subscription: {}", configuration.getBrokerName(), sub); 1186 } 1187 } 1188 } 1189 1190 private void undoMapRegistration(DemandSubscription sub) { 1191 subscriptionMapByLocalId.remove(sub.getLocalInfo().getConsumerId()); 1192 subscriptionMapByRemoteId.remove(sub.getRemoteInfo().getConsumerId()); 1193 } 1194 1195 /* 1196 * check our existing subs networkConsumerIds against the list of network 1197 * ids in this subscription A match means a duplicate which we suppress for 1198 * topics and maybe for queues 1199 */ 1200 private boolean duplicateSuppressionIsRequired(DemandSubscription candidate) { 1201 final ConsumerInfo consumerInfo = candidate.getRemoteInfo(); 1202 boolean suppress = false; 1203 1204 if (consumerInfo.getDestination().isQueue() && !configuration.isSuppressDuplicateQueueSubscriptions() || consumerInfo.getDestination().isTopic() 1205 && !configuration.isSuppressDuplicateTopicSubscriptions()) { 1206 return suppress; 1207 } 1208 1209 List<ConsumerId> candidateConsumers = consumerInfo.getNetworkConsumerIds(); 1210 Collection<Subscription> currentSubs = getRegionSubscriptions(consumerInfo.getDestination()); 1211 for (Subscription sub : currentSubs) { 1212 List<ConsumerId> networkConsumers = sub.getConsumerInfo().getNetworkConsumerIds(); 1213 if (!networkConsumers.isEmpty()) { 1214 if (matchFound(candidateConsumers, networkConsumers)) { 1215 if (isInActiveDurableSub(sub)) { 1216 suppress = false; 1217 } else { 1218 suppress = hasLowerPriority(sub, candidate.getLocalInfo()); 1219 } 1220 break; 1221 } 1222 } 1223 } 1224 return suppress; 1225 } 1226 1227 private boolean isInActiveDurableSub(Subscription sub) { 1228 return (sub.getConsumerInfo().isDurable() && sub instanceof DurableTopicSubscription && !((DurableTopicSubscription) sub).isActive()); 1229 } 1230 1231 private boolean hasLowerPriority(Subscription existingSub, ConsumerInfo candidateInfo) { 1232 boolean suppress = false; 1233 1234 if (existingSub.getConsumerInfo().getPriority() >= candidateInfo.getPriority()) { 1235 LOG.debug("{} Ignoring duplicate subscription from {}, sub: {} is duplicate by network subscription with equal or higher network priority: {}, networkConsumerIds: {}", new Object[]{ 1236 configuration.getBrokerName(), remoteBrokerName, candidateInfo, existingSub, existingSub.getConsumerInfo().getNetworkConsumerIds() 1237 }); 1238 suppress = true; 1239 } else { 1240 // remove the existing lower priority duplicate and allow this candidate 1241 try { 1242 removeDuplicateSubscription(existingSub); 1243 1244 LOG.debug("{} Replacing duplicate subscription {} with sub from {}, which has a higher priority, new sub: {}, networkConsumerIds: {}", new Object[]{ 1245 configuration.getBrokerName(), existingSub.getConsumerInfo(), remoteBrokerName, candidateInfo, candidateInfo.getNetworkConsumerIds() 1246 }); 1247 } catch (IOException e) { 1248 LOG.error("Failed to remove duplicated sub as a result of sub with higher priority, sub: {}", existingSub, e); 1249 } 1250 } 1251 return suppress; 1252 } 1253 1254 private void removeDuplicateSubscription(Subscription existingSub) throws IOException { 1255 for (NetworkConnector connector : brokerService.getNetworkConnectors()) { 1256 if (connector.removeDemandSubscription(existingSub.getConsumerInfo().getConsumerId())) { 1257 break; 1258 } 1259 } 1260 } 1261 1262 private boolean matchFound(List<ConsumerId> candidateConsumers, List<ConsumerId> networkConsumers) { 1263 boolean found = false; 1264 for (ConsumerId aliasConsumer : networkConsumers) { 1265 if (candidateConsumers.contains(aliasConsumer)) { 1266 found = true; 1267 break; 1268 } 1269 } 1270 return found; 1271 } 1272 1273 protected final Collection<Subscription> getRegionSubscriptions(ActiveMQDestination dest) { 1274 RegionBroker region_broker = (RegionBroker) brokerService.getRegionBroker(); 1275 Region region; 1276 Collection<Subscription> subs; 1277 1278 region = null; 1279 switch (dest.getDestinationType()) { 1280 case ActiveMQDestination.QUEUE_TYPE: 1281 region = region_broker.getQueueRegion(); 1282 break; 1283 case ActiveMQDestination.TOPIC_TYPE: 1284 region = region_broker.getTopicRegion(); 1285 break; 1286 case ActiveMQDestination.TEMP_QUEUE_TYPE: 1287 region = region_broker.getTempQueueRegion(); 1288 break; 1289 case ActiveMQDestination.TEMP_TOPIC_TYPE: 1290 region = region_broker.getTempTopicRegion(); 1291 break; 1292 } 1293 1294 if (region instanceof AbstractRegion) { 1295 subs = ((AbstractRegion) region).getSubscriptions().values(); 1296 } else { 1297 subs = null; 1298 } 1299 1300 return subs; 1301 } 1302 1303 protected DemandSubscription createDemandSubscription(ConsumerInfo info) throws IOException { 1304 // add our original id to ourselves 1305 info.addNetworkConsumerId(info.getConsumerId()); 1306 return doCreateDemandSubscription(info); 1307 } 1308 1309 protected DemandSubscription doCreateDemandSubscription(ConsumerInfo info) throws IOException { 1310 DemandSubscription result = new DemandSubscription(info); 1311 result.getLocalInfo().setConsumerId(new ConsumerId(localSessionInfo.getSessionId(), consumerIdGenerator.getNextSequenceId())); 1312 if (info.getDestination().isTemporary()) { 1313 // reset the local connection Id 1314 ActiveMQTempDestination dest = (ActiveMQTempDestination) result.getLocalInfo().getDestination(); 1315 dest.setConnectionId(localConnectionInfo.getConnectionId().toString()); 1316 } 1317 1318 if (configuration.isDecreaseNetworkConsumerPriority()) { 1319 byte priority = (byte) configuration.getConsumerPriorityBase(); 1320 if (info.getBrokerPath() != null && info.getBrokerPath().length > 1) { 1321 // The longer the path to the consumer, the less it's consumer priority. 1322 priority -= info.getBrokerPath().length + 1; 1323 } 1324 result.getLocalInfo().setPriority(priority); 1325 LOG.debug("{} using priority: {} for subscription: {}", new Object[]{configuration.getBrokerName(), priority, info}); 1326 } 1327 configureDemandSubscription(info, result); 1328 return result; 1329 } 1330 1331 final protected DemandSubscription createDemandSubscription(ActiveMQDestination destination) { 1332 ConsumerInfo info = new ConsumerInfo(); 1333 info.setNetworkSubscription(true); 1334 info.setDestination(destination); 1335 1336 // Indicate that this subscription is being made on behalf of the remote broker. 1337 info.setBrokerPath(new BrokerId[]{remoteBrokerId}); 1338 1339 // the remote info held by the DemandSubscription holds the original 1340 // consumerId, the local info get's overwritten 1341 info.setConsumerId(new ConsumerId(localSessionInfo.getSessionId(), consumerIdGenerator.getNextSequenceId())); 1342 DemandSubscription result = null; 1343 try { 1344 result = createDemandSubscription(info); 1345 } catch (IOException e) { 1346 LOG.error("Failed to create DemandSubscription ", e); 1347 } 1348 return result; 1349 } 1350 1351 protected void configureDemandSubscription(ConsumerInfo info, DemandSubscription sub) throws IOException { 1352 if (AdvisorySupport.isConsumerAdvisoryTopic(info.getDestination())) { 1353 sub.getLocalInfo().setDispatchAsync(true); 1354 } else { 1355 sub.getLocalInfo().setDispatchAsync(configuration.isDispatchAsync()); 1356 } 1357 sub.getLocalInfo().setPrefetchSize(configuration.getPrefetchSize()); 1358 subscriptionMapByLocalId.put(sub.getLocalInfo().getConsumerId(), sub); 1359 subscriptionMapByRemoteId.put(sub.getRemoteInfo().getConsumerId(), sub); 1360 1361 sub.setNetworkBridgeFilter(createNetworkBridgeFilter(info)); 1362 if (!info.isDurable()) { 1363 // This works for now since we use a VM connection to the local broker. 1364 // may need to change if we ever subscribe to a remote broker. 1365 sub.getLocalInfo().setAdditionalPredicate(sub.getNetworkBridgeFilter()); 1366 } else { 1367 sub.setLocalDurableSubscriber(new SubscriptionInfo(info.getClientId(), info.getSubscriptionName())); 1368 } 1369 } 1370 1371 protected void removeDemandSubscription(ConsumerId id) throws IOException { 1372 DemandSubscription sub = subscriptionMapByRemoteId.remove(id); 1373 LOG.debug("{} remove request on {} from {}, consumer id: {}, matching sub: {}", new Object[]{ 1374 configuration.getBrokerName(), localBroker, remoteBrokerName, id, sub 1375 }); 1376 if (sub != null) { 1377 removeSubscription(sub); 1378 LOG.debug("{} removed sub on {} from {}: {}", new Object[]{ 1379 configuration.getBrokerName(), localBroker, remoteBrokerName, sub.getRemoteInfo() 1380 }); 1381 } 1382 } 1383 1384 protected boolean removeDemandSubscriptionByLocalId(ConsumerId consumerId) { 1385 boolean removeDone = false; 1386 DemandSubscription sub = subscriptionMapByLocalId.get(consumerId); 1387 if (sub != null) { 1388 try { 1389 removeDemandSubscription(sub.getRemoteInfo().getConsumerId()); 1390 removeDone = true; 1391 } catch (IOException e) { 1392 LOG.debug("removeDemandSubscriptionByLocalId failed for localId: {}", consumerId, e); 1393 } 1394 } 1395 return removeDone; 1396 } 1397 1398 /** 1399 * Performs a timed wait on the started latch and then checks for disposed 1400 * before performing another wait each time the the started wait times out. 1401 */ 1402 protected boolean safeWaitUntilStarted() throws InterruptedException { 1403 while (!disposed.get()) { 1404 if (startedLatch.await(1, TimeUnit.SECONDS)) { 1405 break; 1406 } 1407 } 1408 return !disposed.get(); 1409 } 1410 1411 protected NetworkBridgeFilter createNetworkBridgeFilter(ConsumerInfo info) throws IOException { 1412 NetworkBridgeFilterFactory filterFactory = defaultFilterFactory; 1413 if (brokerService != null && brokerService.getDestinationPolicy() != null) { 1414 PolicyEntry entry = brokerService.getDestinationPolicy().getEntryFor(info.getDestination()); 1415 if (entry != null && entry.getNetworkBridgeFilterFactory() != null) { 1416 filterFactory = entry.getNetworkBridgeFilterFactory(); 1417 } 1418 } 1419 return filterFactory.create(info, getRemoteBrokerPath(), configuration.getMessageTTL(), configuration.getConsumerTTL()); 1420 } 1421 1422 protected void addRemoteBrokerToBrokerPath(ConsumerInfo info) throws IOException { 1423 info.setBrokerPath(appendToBrokerPath(info.getBrokerPath(), getRemoteBrokerPath())); 1424 } 1425 1426 protected BrokerId[] getRemoteBrokerPath() { 1427 return remoteBrokerPath; 1428 } 1429 1430 @Override 1431 public void setNetworkBridgeListener(NetworkBridgeListener listener) { 1432 this.networkBridgeListener = listener; 1433 } 1434 1435 private void fireBridgeFailed(Throwable reason) { 1436 LOG.trace("fire bridge failed, listener: {}", this.networkBridgeListener, reason); 1437 NetworkBridgeListener l = this.networkBridgeListener; 1438 if (l != null && this.bridgeFailed.compareAndSet(false, true)) { 1439 l.bridgeFailed(); 1440 } 1441 } 1442 1443 /** 1444 * @return Returns the dynamicallyIncludedDestinations. 1445 */ 1446 public ActiveMQDestination[] getDynamicallyIncludedDestinations() { 1447 return dynamicallyIncludedDestinations; 1448 } 1449 1450 /** 1451 * @param dynamicallyIncludedDestinations 1452 * The dynamicallyIncludedDestinations to set. 1453 */ 1454 public void setDynamicallyIncludedDestinations(ActiveMQDestination[] dynamicallyIncludedDestinations) { 1455 this.dynamicallyIncludedDestinations = dynamicallyIncludedDestinations; 1456 } 1457 1458 /** 1459 * @return Returns the excludedDestinations. 1460 */ 1461 public ActiveMQDestination[] getExcludedDestinations() { 1462 return excludedDestinations; 1463 } 1464 1465 /** 1466 * @param excludedDestinations The excludedDestinations to set. 1467 */ 1468 public void setExcludedDestinations(ActiveMQDestination[] excludedDestinations) { 1469 this.excludedDestinations = excludedDestinations; 1470 } 1471 1472 /** 1473 * @return Returns the staticallyIncludedDestinations. 1474 */ 1475 public ActiveMQDestination[] getStaticallyIncludedDestinations() { 1476 return staticallyIncludedDestinations; 1477 } 1478 1479 /** 1480 * @param staticallyIncludedDestinations The staticallyIncludedDestinations to set. 1481 */ 1482 public void setStaticallyIncludedDestinations(ActiveMQDestination[] staticallyIncludedDestinations) { 1483 this.staticallyIncludedDestinations = staticallyIncludedDestinations; 1484 } 1485 1486 /** 1487 * @return Returns the durableDestinations. 1488 */ 1489 public ActiveMQDestination[] getDurableDestinations() { 1490 return durableDestinations; 1491 } 1492 1493 /** 1494 * @param durableDestinations The durableDestinations to set. 1495 */ 1496 public void setDurableDestinations(ActiveMQDestination[] durableDestinations) { 1497 this.durableDestinations = durableDestinations; 1498 } 1499 1500 /** 1501 * @return Returns the localBroker. 1502 */ 1503 public Transport getLocalBroker() { 1504 return localBroker; 1505 } 1506 1507 /** 1508 * @return Returns the remoteBroker. 1509 */ 1510 public Transport getRemoteBroker() { 1511 return remoteBroker; 1512 } 1513 1514 /** 1515 * @return the createdByDuplex 1516 */ 1517 public boolean isCreatedByDuplex() { 1518 return this.createdByDuplex; 1519 } 1520 1521 /** 1522 * @param createdByDuplex the createdByDuplex to set 1523 */ 1524 public void setCreatedByDuplex(boolean createdByDuplex) { 1525 this.createdByDuplex = createdByDuplex; 1526 } 1527 1528 @Override 1529 public String getRemoteAddress() { 1530 return remoteBroker.getRemoteAddress(); 1531 } 1532 1533 @Override 1534 public String getLocalAddress() { 1535 return localBroker.getRemoteAddress(); 1536 } 1537 1538 @Override 1539 public String getRemoteBrokerName() { 1540 return remoteBrokerInfo == null ? null : remoteBrokerInfo.getBrokerName(); 1541 } 1542 1543 @Override 1544 public String getRemoteBrokerId() { 1545 return (remoteBrokerInfo == null || remoteBrokerInfo.getBrokerId() == null) ? null : remoteBrokerInfo.getBrokerId().toString(); 1546 } 1547 1548 @Override 1549 public String getLocalBrokerName() { 1550 return localBrokerInfo == null ? null : localBrokerInfo.getBrokerName(); 1551 } 1552 1553 @Override 1554 public long getDequeueCounter() { 1555 return dequeueCounter.get(); 1556 } 1557 1558 @Override 1559 public long getEnqueueCounter() { 1560 return enqueueCounter.get(); 1561 } 1562 1563 protected boolean isDuplex() { 1564 return configuration.isDuplex() || createdByDuplex; 1565 } 1566 1567 public ConcurrentHashMap<ConsumerId, DemandSubscription> getLocalSubscriptionMap() { 1568 return subscriptionMapByRemoteId; 1569 } 1570 1571 @Override 1572 public void setBrokerService(BrokerService brokerService) { 1573 this.brokerService = brokerService; 1574 this.localBrokerId = brokerService.getRegionBroker().getBrokerId(); 1575 localBrokerPath[0] = localBrokerId; 1576 } 1577 1578 @Override 1579 public void setMbeanObjectName(ObjectName objectName) { 1580 this.mbeanObjectName = objectName; 1581 } 1582 1583 @Override 1584 public ObjectName getMbeanObjectName() { 1585 return mbeanObjectName; 1586 } 1587 1588 @Override 1589 public void resetStats() { 1590 enqueueCounter.set(0); 1591 dequeueCounter.set(0); 1592 } 1593 1594 /* 1595 * Used to allow for async tasks to await receipt of the BrokerInfo from the local and 1596 * remote sides of the network bridge. 1597 */ 1598 private static class FutureBrokerInfo implements Future<BrokerInfo> { 1599 1600 private final CountDownLatch slot = new CountDownLatch(1); 1601 private final AtomicBoolean disposed; 1602 private volatile BrokerInfo info = null; 1603 1604 public FutureBrokerInfo(BrokerInfo info, AtomicBoolean disposed) { 1605 this.info = info; 1606 this.disposed = disposed; 1607 } 1608 1609 @Override 1610 public boolean cancel(boolean mayInterruptIfRunning) { 1611 slot.countDown(); 1612 return true; 1613 } 1614 1615 @Override 1616 public boolean isCancelled() { 1617 return slot.getCount() == 0 && info == null; 1618 } 1619 1620 @Override 1621 public boolean isDone() { 1622 return info != null; 1623 } 1624 1625 @Override 1626 public BrokerInfo get() throws InterruptedException, ExecutionException { 1627 try { 1628 if (info == null) { 1629 while (!disposed.get()) { 1630 if (slot.await(1, TimeUnit.SECONDS)) { 1631 break; 1632 } 1633 } 1634 } 1635 return info; 1636 } catch (InterruptedException e) { 1637 Thread.currentThread().interrupt(); 1638 LOG.debug("Operation interrupted: {}", e, e); 1639 throw new InterruptedException("Interrupted."); 1640 } 1641 } 1642 1643 @Override 1644 public BrokerInfo get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { 1645 try { 1646 if (info == null) { 1647 long deadline = System.currentTimeMillis() + unit.toMillis(timeout); 1648 1649 while (!disposed.get() || System.currentTimeMillis() < deadline) { 1650 if (slot.await(1, TimeUnit.MILLISECONDS)) { 1651 break; 1652 } 1653 } 1654 if (info == null) { 1655 throw new TimeoutException(); 1656 } 1657 } 1658 return info; 1659 } catch (InterruptedException e) { 1660 throw new InterruptedException("Interrupted."); 1661 } 1662 } 1663 1664 public void set(BrokerInfo info) { 1665 this.info = info; 1666 this.slot.countDown(); 1667 } 1668 } 1669 1670 protected void serviceOutbound(Message message) { 1671 NetworkBridgeListener l = this.networkBridgeListener; 1672 if (l != null) { 1673 l.onOutboundMessage(this, message); 1674 } 1675 } 1676 1677 protected void serviceInboundMessage(Message message) { 1678 NetworkBridgeListener l = this.networkBridgeListener; 1679 if (l != null) { 1680 l.onInboundMessage(this, message); 1681 } 1682 } 1683 1684 protected boolean canDuplexDispatch(Message message) { 1685 boolean result = true; 1686 if (configuration.isCheckDuplicateMessagesOnDuplex()){ 1687 final long producerSequenceId = message.getMessageId().getProducerSequenceId(); 1688 // messages are multiplexed on this producer so we need to query the persistenceAdapter 1689 long lastStoredForMessageProducer = getStoredSequenceIdForMessage(message.getMessageId()); 1690 if (producerSequenceId <= lastStoredForMessageProducer) { 1691 result = false; 1692 LOG.debug("suppressing duplicate message send [{}] from network producer with producerSequence [{}] less than last stored: {}", new Object[]{ 1693 (LOG.isTraceEnabled() ? message : message.getMessageId()), producerSequenceId, lastStoredForMessageProducer 1694 }); 1695 } 1696 } 1697 return result; 1698 } 1699 1700 protected long getStoredSequenceIdForMessage(MessageId messageId) { 1701 try { 1702 return brokerService.getPersistenceAdapter().getLastProducerSequenceId(messageId.getProducerId()); 1703 } catch (IOException ignored) { 1704 LOG.debug("Failed to determine last producer sequence id for: {}", messageId, ignored); 1705 } 1706 return -1; 1707 } 1708 1709}