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