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