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