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