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