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.broker;
018
019import java.io.EOFException;
020import java.io.IOException;
021import java.net.SocketException;
022import java.net.URI;
023import java.util.Collection;
024import java.util.HashMap;
025import java.util.Iterator;
026import java.util.LinkedList;
027import java.util.List;
028import java.util.Map;
029import java.util.Properties;
030import java.util.concurrent.ConcurrentHashMap;
031import java.util.concurrent.CopyOnWriteArrayList;
032import java.util.concurrent.CountDownLatch;
033import java.util.concurrent.TimeUnit;
034import java.util.concurrent.atomic.AtomicBoolean;
035import java.util.concurrent.atomic.AtomicInteger;
036import java.util.concurrent.atomic.AtomicReference;
037import java.util.concurrent.locks.ReentrantReadWriteLock;
038
039import javax.transaction.xa.XAResource;
040
041import org.apache.activemq.advisory.AdvisorySupport;
042import org.apache.activemq.broker.region.ConnectionStatistics;
043import org.apache.activemq.broker.region.RegionBroker;
044import org.apache.activemq.command.ActiveMQDestination;
045import org.apache.activemq.command.BrokerInfo;
046import org.apache.activemq.command.Command;
047import org.apache.activemq.command.CommandTypes;
048import org.apache.activemq.command.ConnectionControl;
049import org.apache.activemq.command.ConnectionError;
050import org.apache.activemq.command.ConnectionId;
051import org.apache.activemq.command.ConnectionInfo;
052import org.apache.activemq.command.ConsumerControl;
053import org.apache.activemq.command.ConsumerId;
054import org.apache.activemq.command.ConsumerInfo;
055import org.apache.activemq.command.ControlCommand;
056import org.apache.activemq.command.DataArrayResponse;
057import org.apache.activemq.command.DestinationInfo;
058import org.apache.activemq.command.ExceptionResponse;
059import org.apache.activemq.command.FlushCommand;
060import org.apache.activemq.command.IntegerResponse;
061import org.apache.activemq.command.KeepAliveInfo;
062import org.apache.activemq.command.Message;
063import org.apache.activemq.command.MessageAck;
064import org.apache.activemq.command.MessageDispatch;
065import org.apache.activemq.command.MessageDispatchNotification;
066import org.apache.activemq.command.MessagePull;
067import org.apache.activemq.command.ProducerAck;
068import org.apache.activemq.command.ProducerId;
069import org.apache.activemq.command.ProducerInfo;
070import org.apache.activemq.command.RemoveSubscriptionInfo;
071import org.apache.activemq.command.Response;
072import org.apache.activemq.command.SessionId;
073import org.apache.activemq.command.SessionInfo;
074import org.apache.activemq.command.ShutdownInfo;
075import org.apache.activemq.command.TransactionId;
076import org.apache.activemq.command.TransactionInfo;
077import org.apache.activemq.command.WireFormatInfo;
078import org.apache.activemq.network.DemandForwardingBridge;
079import org.apache.activemq.network.MBeanNetworkListener;
080import org.apache.activemq.network.NetworkBridgeConfiguration;
081import org.apache.activemq.network.NetworkBridgeFactory;
082import org.apache.activemq.security.MessageAuthorizationPolicy;
083import org.apache.activemq.state.CommandVisitor;
084import org.apache.activemq.state.ConnectionState;
085import org.apache.activemq.state.ConsumerState;
086import org.apache.activemq.state.ProducerState;
087import org.apache.activemq.state.SessionState;
088import org.apache.activemq.state.TransactionState;
089import org.apache.activemq.thread.Task;
090import org.apache.activemq.thread.TaskRunner;
091import org.apache.activemq.thread.TaskRunnerFactory;
092import org.apache.activemq.transaction.Transaction;
093import org.apache.activemq.transport.DefaultTransportListener;
094import org.apache.activemq.transport.ResponseCorrelator;
095import org.apache.activemq.transport.TransmitCallback;
096import org.apache.activemq.transport.Transport;
097import org.apache.activemq.transport.TransportDisposedIOException;
098import org.apache.activemq.util.IntrospectionSupport;
099import org.apache.activemq.util.MarshallingSupport;
100import org.slf4j.Logger;
101import org.slf4j.LoggerFactory;
102import org.slf4j.MDC;
103
104public class TransportConnection implements Connection, Task, CommandVisitor {
105    private static final Logger LOG = LoggerFactory.getLogger(TransportConnection.class);
106    private static final Logger TRANSPORTLOG = LoggerFactory.getLogger(TransportConnection.class.getName() + ".Transport");
107    private static final Logger SERVICELOG = LoggerFactory.getLogger(TransportConnection.class.getName() + ".Service");
108    // Keeps track of the broker and connector that created this connection.
109    protected final Broker broker;
110    protected final TransportConnector connector;
111    // Keeps track of the state of the connections.
112    // protected final ConcurrentHashMap localConnectionStates=new
113    // ConcurrentHashMap();
114    protected final Map<ConnectionId, ConnectionState> brokerConnectionStates;
115    // The broker and wireformat info that was exchanged.
116    protected BrokerInfo brokerInfo;
117    protected final List<Command> dispatchQueue = new LinkedList<Command>();
118    protected TaskRunner taskRunner;
119    protected final AtomicReference<IOException> transportException = new AtomicReference<IOException>();
120    protected AtomicBoolean dispatchStopped = new AtomicBoolean(false);
121    private final Transport transport;
122    private MessageAuthorizationPolicy messageAuthorizationPolicy;
123    private WireFormatInfo wireFormatInfo;
124    // Used to do async dispatch.. this should perhaps be pushed down into the
125    // transport layer..
126    private boolean inServiceException;
127    private final ConnectionStatistics statistics = new ConnectionStatistics();
128    private boolean manageable;
129    private boolean slow;
130    private boolean markedCandidate;
131    private boolean blockedCandidate;
132    private boolean blocked;
133    private boolean connected;
134    private boolean active;
135    private boolean starting;
136    private boolean pendingStop;
137    private long timeStamp;
138    private final AtomicBoolean stopping = new AtomicBoolean(false);
139    private final CountDownLatch stopped = new CountDownLatch(1);
140    private final AtomicBoolean asyncException = new AtomicBoolean(false);
141    private final Map<ProducerId, ProducerBrokerExchange> producerExchanges = new HashMap<ProducerId, ProducerBrokerExchange>();
142    private final Map<ConsumerId, ConsumerBrokerExchange> consumerExchanges = new HashMap<ConsumerId, ConsumerBrokerExchange>();
143    private final CountDownLatch dispatchStoppedLatch = new CountDownLatch(1);
144    private ConnectionContext context;
145    private boolean networkConnection;
146    private boolean faultTolerantConnection;
147    private final AtomicInteger protocolVersion = new AtomicInteger(CommandTypes.PROTOCOL_VERSION);
148    private DemandForwardingBridge duplexBridge;
149    private final TaskRunnerFactory taskRunnerFactory;
150    private final TaskRunnerFactory stopTaskRunnerFactory;
151    private TransportConnectionStateRegister connectionStateRegister = new SingleTransportConnectionStateRegister();
152    private final ReentrantReadWriteLock serviceLock = new ReentrantReadWriteLock();
153    private String duplexNetworkConnectorId;
154    private Throwable stopError = null;
155
156    /**
157     * @param taskRunnerFactory - can be null if you want direct dispatch to the transport
158     *                          else commands are sent async.
159     * @param stopTaskRunnerFactory - can <b>not</b> be null, used for stopping this connection.
160     */
161    public TransportConnection(TransportConnector connector, final Transport transport, Broker broker,
162                               TaskRunnerFactory taskRunnerFactory, TaskRunnerFactory stopTaskRunnerFactory) {
163        this.connector = connector;
164        this.broker = broker;
165        RegionBroker rb = (RegionBroker) broker.getAdaptor(RegionBroker.class);
166        brokerConnectionStates = rb.getConnectionStates();
167        if (connector != null) {
168            this.statistics.setParent(connector.getStatistics());
169            this.messageAuthorizationPolicy = connector.getMessageAuthorizationPolicy();
170        }
171        this.taskRunnerFactory = taskRunnerFactory;
172        this.stopTaskRunnerFactory = stopTaskRunnerFactory;
173        this.transport = transport;
174        final BrokerService brokerService = this.broker.getBrokerService();
175        if( this.transport instanceof BrokerServiceAware ) {
176            ((BrokerServiceAware)this.transport).setBrokerService(brokerService);
177        }
178        this.transport.setTransportListener(new DefaultTransportListener() {
179            @Override
180            public void onCommand(Object o) {
181                serviceLock.readLock().lock();
182                try {
183                    if (!(o instanceof Command)) {
184                        throw new RuntimeException("Protocol violation - Command corrupted: " + o.toString());
185                    }
186                    Command command = (Command) o;
187                    if (!brokerService.isStopping()) {
188                        Response response = service(command);
189                        if (response != null && !brokerService.isStopping()) {
190                            dispatchSync(response);
191                        }
192                    } else {
193                        throw new BrokerStoppedException("Broker " + brokerService + " is being stopped");
194                    }
195                } finally {
196                    serviceLock.readLock().unlock();
197                }
198            }
199
200            @Override
201            public void onException(IOException exception) {
202                serviceLock.readLock().lock();
203                try {
204                    serviceTransportException(exception);
205                } finally {
206                    serviceLock.readLock().unlock();
207                }
208            }
209        });
210        connected = true;
211    }
212
213    /**
214     * Returns the number of messages to be dispatched to this connection
215     *
216     * @return size of dispatch queue
217     */
218    @Override
219    public int getDispatchQueueSize() {
220        synchronized (dispatchQueue) {
221            return dispatchQueue.size();
222        }
223    }
224
225    public void serviceTransportException(IOException e) {
226        BrokerService bService = connector.getBrokerService();
227        if (bService.isShutdownOnSlaveFailure()) {
228            if (brokerInfo != null) {
229                if (brokerInfo.isSlaveBroker()) {
230                    LOG.error("Slave has exception: {} shutting down master now.", e.getMessage(), e);
231                    try {
232                        doStop();
233                        bService.stop();
234                    } catch (Exception ex) {
235                        LOG.warn("Failed to stop the master", ex);
236                    }
237                }
238            }
239        }
240        if (!stopping.get() && !pendingStop) {
241            transportException.set(e);
242            if (TRANSPORTLOG.isDebugEnabled()) {
243                TRANSPORTLOG.debug(this + " failed: " + e, e);
244            } else if (TRANSPORTLOG.isWarnEnabled() && !expected(e)) {
245                TRANSPORTLOG.warn(this + " failed: " + e);
246            }
247            stopAsync();
248        }
249    }
250
251    private boolean expected(IOException e) {
252        return isStomp() && ((e instanceof SocketException && e.getMessage().indexOf("reset") != -1) || e instanceof EOFException);
253    }
254
255    private boolean isStomp() {
256        URI uri = connector.getUri();
257        return uri != null && uri.getScheme() != null && uri.getScheme().indexOf("stomp") != -1;
258    }
259
260    /**
261     * Calls the serviceException method in an async thread. Since handling a
262     * service exception closes a socket, we should not tie up broker threads
263     * since client sockets may hang or cause deadlocks.
264     */
265    @Override
266    public void serviceExceptionAsync(final IOException e) {
267        if (asyncException.compareAndSet(false, true)) {
268            new Thread("Async Exception Handler") {
269                @Override
270                public void run() {
271                    serviceException(e);
272                }
273            }.start();
274        }
275    }
276
277    /**
278     * Closes a clients connection due to a detected error. Errors are ignored
279     * if: the client is closing or broker is closing. Otherwise, the connection
280     * error transmitted to the client before stopping it's transport.
281     */
282    @Override
283    public void serviceException(Throwable e) {
284        // are we a transport exception such as not being able to dispatch
285        // synchronously to a transport
286        if (e instanceof IOException) {
287            serviceTransportException((IOException) e);
288        } else if (e.getClass() == BrokerStoppedException.class) {
289            // Handle the case where the broker is stopped
290            // But the client is still connected.
291            if (!stopping.get()) {
292                SERVICELOG.debug("Broker has been stopped.  Notifying client and closing his connection.");
293                ConnectionError ce = new ConnectionError();
294                ce.setException(e);
295                dispatchSync(ce);
296                // Record the error that caused the transport to stop
297                this.stopError = e;
298                // Wait a little bit to try to get the output buffer to flush
299                // the exception notification to the client.
300                try {
301                    Thread.sleep(500);
302                } catch (InterruptedException ie) {
303                    Thread.currentThread().interrupt();
304                }
305                // Worst case is we just kill the connection before the
306                // notification gets to him.
307                stopAsync();
308            }
309        } else if (!stopping.get() && !inServiceException) {
310            inServiceException = true;
311            try {
312                SERVICELOG.warn("Async error occurred: ", e);
313                ConnectionError ce = new ConnectionError();
314                ce.setException(e);
315                if (pendingStop) {
316                    dispatchSync(ce);
317                } else {
318                    dispatchAsync(ce);
319                }
320            } finally {
321                inServiceException = false;
322            }
323        }
324    }
325
326    @Override
327    public Response service(Command command) {
328        MDC.put("activemq.connector", connector.getUri().toString());
329        Response response = null;
330        boolean responseRequired = command.isResponseRequired();
331        int commandId = command.getCommandId();
332        try {
333            if (!pendingStop) {
334                response = command.visit(this);
335            } else {
336                response = new ExceptionResponse(this.stopError);
337            }
338        } catch (Throwable e) {
339            if (SERVICELOG.isDebugEnabled() && e.getClass() != BrokerStoppedException.class) {
340                SERVICELOG.debug("Error occured while processing " + (responseRequired ? "sync" : "async")
341                        + " command: " + command + ", exception: " + e, e);
342            }
343
344            if (e instanceof SuppressReplyException || (e.getCause() instanceof SuppressReplyException)) {
345                LOG.info("Suppressing reply to: " + command + " on: " + e + ", cause: " + e.getCause());
346                responseRequired = false;
347            }
348
349            if (responseRequired) {
350                if (e instanceof SecurityException || e.getCause() instanceof SecurityException) {
351                    SERVICELOG.warn("Security Error occurred: {}", e.getMessage());
352                }
353                response = new ExceptionResponse(e);
354            } else {
355                serviceException(e);
356            }
357        }
358        if (responseRequired) {
359            if (response == null) {
360                response = new Response();
361            }
362            response.setCorrelationId(commandId);
363        }
364        // The context may have been flagged so that the response is not
365        // sent.
366        if (context != null) {
367            if (context.isDontSendReponse()) {
368                context.setDontSendReponse(false);
369                response = null;
370            }
371            context = null;
372        }
373        MDC.remove("activemq.connector");
374        return response;
375    }
376
377    @Override
378    public Response processKeepAlive(KeepAliveInfo info) throws Exception {
379        return null;
380    }
381
382    @Override
383    public Response processRemoveSubscription(RemoveSubscriptionInfo info) throws Exception {
384        broker.removeSubscription(lookupConnectionState(info.getConnectionId()).getContext(), info);
385        return null;
386    }
387
388    @Override
389    public Response processWireFormat(WireFormatInfo info) throws Exception {
390        wireFormatInfo = info;
391        protocolVersion.set(info.getVersion());
392        return null;
393    }
394
395    @Override
396    public Response processShutdown(ShutdownInfo info) throws Exception {
397        stopAsync();
398        return null;
399    }
400
401    @Override
402    public Response processFlush(FlushCommand command) throws Exception {
403        return null;
404    }
405
406    @Override
407    public Response processBeginTransaction(TransactionInfo info) throws Exception {
408        TransportConnectionState cs = lookupConnectionState(info.getConnectionId());
409        context = null;
410        if (cs != null) {
411            context = cs.getContext();
412        }
413        if (cs == null) {
414            throw new NullPointerException("Context is null");
415        }
416        // Avoid replaying dup commands
417        if (cs.getTransactionState(info.getTransactionId()) == null) {
418            cs.addTransactionState(info.getTransactionId());
419            broker.beginTransaction(context, info.getTransactionId());
420        }
421        return null;
422    }
423
424    @Override
425    public int getActiveTransactionCount() {
426        int rc = 0;
427        for (TransportConnectionState cs : connectionStateRegister.listConnectionStates()) {
428            Collection<TransactionState> transactions = cs.getTransactionStates();
429            for (TransactionState transaction : transactions) {
430                rc++;
431            }
432        }
433        return rc;
434    }
435
436    @Override
437    public Long getOldestActiveTransactionDuration() {
438        TransactionState oldestTX = null;
439        for (TransportConnectionState cs : connectionStateRegister.listConnectionStates()) {
440            Collection<TransactionState> transactions = cs.getTransactionStates();
441            for (TransactionState transaction : transactions) {
442                if( oldestTX ==null || oldestTX.getCreatedAt() < transaction.getCreatedAt() ) {
443                    oldestTX = transaction;
444                }
445            }
446        }
447        if( oldestTX == null ) {
448            return null;
449        }
450        return System.currentTimeMillis() - oldestTX.getCreatedAt();
451    }
452
453    @Override
454    public Response processEndTransaction(TransactionInfo info) throws Exception {
455        // No need to do anything. This packet is just sent by the client
456        // make sure he is synced with the server as commit command could
457        // come from a different connection.
458        return null;
459    }
460
461    @Override
462    public Response processPrepareTransaction(TransactionInfo info) throws Exception {
463        TransportConnectionState cs = lookupConnectionState(info.getConnectionId());
464        context = null;
465        if (cs != null) {
466            context = cs.getContext();
467        }
468        if (cs == null) {
469            throw new NullPointerException("Context is null");
470        }
471        TransactionState transactionState = cs.getTransactionState(info.getTransactionId());
472        if (transactionState == null) {
473            throw new IllegalStateException("Cannot prepare a transaction that had not been started or previously returned XA_RDONLY: "
474                    + info.getTransactionId());
475        }
476        // Avoid dups.
477        if (!transactionState.isPrepared()) {
478            transactionState.setPrepared(true);
479            int result = broker.prepareTransaction(context, info.getTransactionId());
480            transactionState.setPreparedResult(result);
481            if (result == XAResource.XA_RDONLY) {
482                // we are done, no further rollback or commit from TM
483                cs.removeTransactionState(info.getTransactionId());
484            }
485            IntegerResponse response = new IntegerResponse(result);
486            return response;
487        } else {
488            IntegerResponse response = new IntegerResponse(transactionState.getPreparedResult());
489            return response;
490        }
491    }
492
493    @Override
494    public Response processCommitTransactionOnePhase(TransactionInfo info) throws Exception {
495        TransportConnectionState cs = lookupConnectionState(info.getConnectionId());
496        context = cs.getContext();
497        cs.removeTransactionState(info.getTransactionId());
498        broker.commitTransaction(context, info.getTransactionId(), true);
499        return null;
500    }
501
502    @Override
503    public Response processCommitTransactionTwoPhase(TransactionInfo info) throws Exception {
504        TransportConnectionState cs = lookupConnectionState(info.getConnectionId());
505        context = cs.getContext();
506        cs.removeTransactionState(info.getTransactionId());
507        broker.commitTransaction(context, info.getTransactionId(), false);
508        return null;
509    }
510
511    @Override
512    public Response processRollbackTransaction(TransactionInfo info) throws Exception {
513        TransportConnectionState cs = lookupConnectionState(info.getConnectionId());
514        context = cs.getContext();
515        cs.removeTransactionState(info.getTransactionId());
516        broker.rollbackTransaction(context, info.getTransactionId());
517        return null;
518    }
519
520    @Override
521    public Response processForgetTransaction(TransactionInfo info) throws Exception {
522        TransportConnectionState cs = lookupConnectionState(info.getConnectionId());
523        context = cs.getContext();
524        broker.forgetTransaction(context, info.getTransactionId());
525        return null;
526    }
527
528    @Override
529    public Response processRecoverTransactions(TransactionInfo info) throws Exception {
530        TransportConnectionState cs = lookupConnectionState(info.getConnectionId());
531        context = cs.getContext();
532        TransactionId[] preparedTransactions = broker.getPreparedTransactions(context);
533        return new DataArrayResponse(preparedTransactions);
534    }
535
536    @Override
537    public Response processMessage(Message messageSend) throws Exception {
538        ProducerId producerId = messageSend.getProducerId();
539        ProducerBrokerExchange producerExchange = getProducerBrokerExchange(producerId);
540        if (producerExchange.canDispatch(messageSend)) {
541            broker.send(producerExchange, messageSend);
542        }
543        return null;
544    }
545
546    @Override
547    public Response processMessageAck(MessageAck ack) throws Exception {
548        ConsumerBrokerExchange consumerExchange = getConsumerBrokerExchange(ack.getConsumerId());
549        if (consumerExchange != null) {
550            broker.acknowledge(consumerExchange, ack);
551        } else if (ack.isInTransaction()) {
552            LOG.warn("no matching consumer, ignoring ack {}", consumerExchange, ack);
553        }
554        return null;
555    }
556
557    @Override
558    public Response processMessagePull(MessagePull pull) throws Exception {
559        return broker.messagePull(lookupConnectionState(pull.getConsumerId()).getContext(), pull);
560    }
561
562    @Override
563    public Response processMessageDispatchNotification(MessageDispatchNotification notification) throws Exception {
564        broker.processDispatchNotification(notification);
565        return null;
566    }
567
568    @Override
569    public Response processAddDestination(DestinationInfo info) throws Exception {
570        TransportConnectionState cs = lookupConnectionState(info.getConnectionId());
571        broker.addDestinationInfo(cs.getContext(), info);
572        if (info.getDestination().isTemporary()) {
573            cs.addTempDestination(info);
574        }
575        return null;
576    }
577
578    @Override
579    public Response processRemoveDestination(DestinationInfo info) throws Exception {
580        TransportConnectionState cs = lookupConnectionState(info.getConnectionId());
581        broker.removeDestinationInfo(cs.getContext(), info);
582        if (info.getDestination().isTemporary()) {
583            cs.removeTempDestination(info.getDestination());
584        }
585        return null;
586    }
587
588    @Override
589    public Response processAddProducer(ProducerInfo info) throws Exception {
590        SessionId sessionId = info.getProducerId().getParentId();
591        ConnectionId connectionId = sessionId.getParentId();
592        TransportConnectionState cs = lookupConnectionState(connectionId);
593        if (cs == null) {
594            throw new IllegalStateException("Cannot add a producer to a connection that had not been registered: "
595                    + connectionId);
596        }
597        SessionState ss = cs.getSessionState(sessionId);
598        if (ss == null) {
599            throw new IllegalStateException("Cannot add a producer to a session that had not been registered: "
600                    + sessionId);
601        }
602        // Avoid replaying dup commands
603        if (!ss.getProducerIds().contains(info.getProducerId())) {
604            ActiveMQDestination destination = info.getDestination();
605            if (destination != null && !AdvisorySupport.isAdvisoryTopic(destination)) {
606                if (getProducerCount(connectionId) >= connector.getMaximumProducersAllowedPerConnection()){
607                    throw new IllegalStateException("Can't add producer on connection " + connectionId + ": at maximum limit: " + connector.getMaximumProducersAllowedPerConnection());
608                }
609            }
610            broker.addProducer(cs.getContext(), info);
611            try {
612                ss.addProducer(info);
613            } catch (IllegalStateException e) {
614                broker.removeProducer(cs.getContext(), info);
615            }
616
617        }
618        return null;
619    }
620
621    @Override
622    public Response processRemoveProducer(ProducerId id) throws Exception {
623        SessionId sessionId = id.getParentId();
624        ConnectionId connectionId = sessionId.getParentId();
625        TransportConnectionState cs = lookupConnectionState(connectionId);
626        SessionState ss = cs.getSessionState(sessionId);
627        if (ss == null) {
628            throw new IllegalStateException("Cannot remove a producer from a session that had not been registered: "
629                    + sessionId);
630        }
631        ProducerState ps = ss.removeProducer(id);
632        if (ps == null) {
633            throw new IllegalStateException("Cannot remove a producer that had not been registered: " + id);
634        }
635        removeProducerBrokerExchange(id);
636        broker.removeProducer(cs.getContext(), ps.getInfo());
637        return null;
638    }
639
640    @Override
641    public Response processAddConsumer(ConsumerInfo info) throws Exception {
642        SessionId sessionId = info.getConsumerId().getParentId();
643        ConnectionId connectionId = sessionId.getParentId();
644        TransportConnectionState cs = lookupConnectionState(connectionId);
645        if (cs == null) {
646            throw new IllegalStateException("Cannot add a consumer to a connection that had not been registered: "
647                    + connectionId);
648        }
649        SessionState ss = cs.getSessionState(sessionId);
650        if (ss == null) {
651            throw new IllegalStateException(broker.getBrokerName()
652                    + " Cannot add a consumer to a session that had not been registered: " + sessionId);
653        }
654        // Avoid replaying dup commands
655        if (!ss.getConsumerIds().contains(info.getConsumerId())) {
656            ActiveMQDestination destination = info.getDestination();
657            if (destination != null && !AdvisorySupport.isAdvisoryTopic(destination)) {
658                if (getConsumerCount(connectionId) >= connector.getMaximumConsumersAllowedPerConnection()){
659                    throw new IllegalStateException("Can't add consumer on connection " + connectionId + ": at maximum limit: " + connector.getMaximumConsumersAllowedPerConnection());
660                }
661            }
662
663            broker.addConsumer(cs.getContext(), info);
664            try {
665                ss.addConsumer(info);
666                addConsumerBrokerExchange(info.getConsumerId());
667            } catch (IllegalStateException e) {
668                broker.removeConsumer(cs.getContext(), info);
669            }
670
671        }
672        return null;
673    }
674
675    @Override
676    public Response processRemoveConsumer(ConsumerId id, long lastDeliveredSequenceId) throws Exception {
677        SessionId sessionId = id.getParentId();
678        ConnectionId connectionId = sessionId.getParentId();
679        TransportConnectionState cs = lookupConnectionState(connectionId);
680        if (cs == null) {
681            throw new IllegalStateException("Cannot remove a consumer from a connection that had not been registered: "
682                    + connectionId);
683        }
684        SessionState ss = cs.getSessionState(sessionId);
685        if (ss == null) {
686            throw new IllegalStateException("Cannot remove a consumer from a session that had not been registered: "
687                    + sessionId);
688        }
689        ConsumerState consumerState = ss.removeConsumer(id);
690        if (consumerState == null) {
691            throw new IllegalStateException("Cannot remove a consumer that had not been registered: " + id);
692        }
693        ConsumerInfo info = consumerState.getInfo();
694        info.setLastDeliveredSequenceId(lastDeliveredSequenceId);
695        broker.removeConsumer(cs.getContext(), consumerState.getInfo());
696        removeConsumerBrokerExchange(id);
697        return null;
698    }
699
700    @Override
701    public Response processAddSession(SessionInfo info) throws Exception {
702        ConnectionId connectionId = info.getSessionId().getParentId();
703        TransportConnectionState cs = lookupConnectionState(connectionId);
704        // Avoid replaying dup commands
705        if (cs != null && !cs.getSessionIds().contains(info.getSessionId())) {
706            broker.addSession(cs.getContext(), info);
707            try {
708                cs.addSession(info);
709            } catch (IllegalStateException e) {
710                e.printStackTrace();
711                broker.removeSession(cs.getContext(), info);
712            }
713        }
714        return null;
715    }
716
717    @Override
718    public Response processRemoveSession(SessionId id, long lastDeliveredSequenceId) throws Exception {
719        ConnectionId connectionId = id.getParentId();
720        TransportConnectionState cs = lookupConnectionState(connectionId);
721        if (cs == null) {
722            throw new IllegalStateException("Cannot remove session from connection that had not been registered: " + connectionId);
723        }
724        SessionState session = cs.getSessionState(id);
725        if (session == null) {
726            throw new IllegalStateException("Cannot remove session that had not been registered: " + id);
727        }
728        // Don't let new consumers or producers get added while we are closing
729        // this down.
730        session.shutdown();
731        // Cascade the connection stop to the consumers and producers.
732        for (ConsumerId consumerId : session.getConsumerIds()) {
733            try {
734                processRemoveConsumer(consumerId, lastDeliveredSequenceId);
735            } catch (Throwable e) {
736                LOG.warn("Failed to remove consumer: {}", consumerId, e);
737            }
738        }
739        for (ProducerId producerId : session.getProducerIds()) {
740            try {
741                processRemoveProducer(producerId);
742            } catch (Throwable e) {
743                LOG.warn("Failed to remove producer: {}", producerId, e);
744            }
745        }
746        cs.removeSession(id);
747        broker.removeSession(cs.getContext(), session.getInfo());
748        return null;
749    }
750
751    @Override
752    public Response processAddConnection(ConnectionInfo info) throws Exception {
753        // Older clients should have been defaulting this field to true.. but
754        // they were not.
755        if (wireFormatInfo != null && wireFormatInfo.getVersion() <= 2) {
756            info.setClientMaster(true);
757        }
758        TransportConnectionState state;
759        // Make sure 2 concurrent connections by the same ID only generate 1
760        // TransportConnectionState object.
761        synchronized (brokerConnectionStates) {
762            state = (TransportConnectionState) brokerConnectionStates.get(info.getConnectionId());
763            if (state == null) {
764                state = new TransportConnectionState(info, this);
765                brokerConnectionStates.put(info.getConnectionId(), state);
766            }
767            state.incrementReference();
768        }
769        // If there are 2 concurrent connections for the same connection id,
770        // then last one in wins, we need to sync here
771        // to figure out the winner.
772        synchronized (state.getConnectionMutex()) {
773            if (state.getConnection() != this) {
774                LOG.debug("Killing previous stale connection: {}", state.getConnection().getRemoteAddress());
775                state.getConnection().stop();
776                LOG.debug("Connection {} taking over previous connection: {}", getRemoteAddress(), state.getConnection().getRemoteAddress());
777                state.setConnection(this);
778                state.reset(info);
779            }
780        }
781        registerConnectionState(info.getConnectionId(), state);
782        LOG.debug("Setting up new connection id: {}, address: {}, info: {}", new Object[]{ info.getConnectionId(), getRemoteAddress(), info });
783        this.faultTolerantConnection = info.isFaultTolerant();
784        // Setup the context.
785        String clientId = info.getClientId();
786        context = new ConnectionContext();
787        context.setBroker(broker);
788        context.setClientId(clientId);
789        context.setClientMaster(info.isClientMaster());
790        context.setConnection(this);
791        context.setConnectionId(info.getConnectionId());
792        context.setConnector(connector);
793        context.setMessageAuthorizationPolicy(getMessageAuthorizationPolicy());
794        context.setNetworkConnection(networkConnection);
795        context.setFaultTolerant(faultTolerantConnection);
796        context.setTransactions(new ConcurrentHashMap<TransactionId, Transaction>());
797        context.setUserName(info.getUserName());
798        context.setWireFormatInfo(wireFormatInfo);
799        context.setReconnect(info.isFailoverReconnect());
800        this.manageable = info.isManageable();
801        context.setConnectionState(state);
802        state.setContext(context);
803        state.setConnection(this);
804        if (info.getClientIp() == null) {
805            info.setClientIp(getRemoteAddress());
806        }
807
808        try {
809            broker.addConnection(context, info);
810        } catch (Exception e) {
811            synchronized (brokerConnectionStates) {
812                brokerConnectionStates.remove(info.getConnectionId());
813            }
814            unregisterConnectionState(info.getConnectionId());
815            LOG.warn("Failed to add Connection {}", info.getConnectionId(), e);
816            if (e instanceof SecurityException) {
817                // close this down - in case the peer of this transport doesn't play nice
818                delayedStop(2000, "Failed with SecurityException: " + e.getLocalizedMessage(), e);
819            }
820            throw e;
821        }
822        if (info.isManageable()) {
823            // send ConnectionCommand
824            ConnectionControl command = this.connector.getConnectionControl();
825            command.setFaultTolerant(broker.isFaultTolerantConfiguration());
826            if (info.isFailoverReconnect()) {
827                command.setRebalanceConnection(false);
828            }
829            dispatchAsync(command);
830        }
831        return null;
832    }
833
834    @Override
835    public synchronized Response processRemoveConnection(ConnectionId id, long lastDeliveredSequenceId)
836            throws InterruptedException {
837        LOG.debug("remove connection id: {}", id);
838        TransportConnectionState cs = lookupConnectionState(id);
839        if (cs != null) {
840            // Don't allow things to be added to the connection state while we
841            // are shutting down.
842            cs.shutdown();
843            // Cascade the connection stop to the sessions.
844            for (SessionId sessionId : cs.getSessionIds()) {
845                try {
846                    processRemoveSession(sessionId, lastDeliveredSequenceId);
847                } catch (Throwable e) {
848                    SERVICELOG.warn("Failed to remove session {}", sessionId, e);
849                }
850            }
851            // Cascade the connection stop to temp destinations.
852            for (Iterator<DestinationInfo> iter = cs.getTempDestinations().iterator(); iter.hasNext(); ) {
853                DestinationInfo di = iter.next();
854                try {
855                    broker.removeDestination(cs.getContext(), di.getDestination(), 0);
856                } catch (Throwable e) {
857                    SERVICELOG.warn("Failed to remove tmp destination {}", di.getDestination(), e);
858                }
859                iter.remove();
860            }
861            try {
862                broker.removeConnection(cs.getContext(), cs.getInfo(), null);
863            } catch (Throwable e) {
864                SERVICELOG.warn("Failed to remove connection {}", cs.getInfo(), e);
865            }
866            TransportConnectionState state = unregisterConnectionState(id);
867            if (state != null) {
868                synchronized (brokerConnectionStates) {
869                    // If we are the last reference, we should remove the state
870                    // from the broker.
871                    if (state.decrementReference() == 0) {
872                        brokerConnectionStates.remove(id);
873                    }
874                }
875            }
876        }
877        return null;
878    }
879
880    @Override
881    public Response processProducerAck(ProducerAck ack) throws Exception {
882        // A broker should not get ProducerAck messages.
883        return null;
884    }
885
886    @Override
887    public Connector getConnector() {
888        return connector;
889    }
890
891    @Override
892    public void dispatchSync(Command message) {
893        try {
894            processDispatch(message);
895        } catch (IOException e) {
896            serviceExceptionAsync(e);
897        }
898    }
899
900    @Override
901    public void dispatchAsync(Command message) {
902        if (!stopping.get()) {
903            if (taskRunner == null) {
904                dispatchSync(message);
905            } else {
906                synchronized (dispatchQueue) {
907                    dispatchQueue.add(message);
908                }
909                try {
910                    taskRunner.wakeup();
911                } catch (InterruptedException e) {
912                    Thread.currentThread().interrupt();
913                }
914            }
915        } else {
916            if (message.isMessageDispatch()) {
917                MessageDispatch md = (MessageDispatch) message;
918                TransmitCallback sub = md.getTransmitCallback();
919                broker.postProcessDispatch(md);
920                if (sub != null) {
921                    sub.onFailure();
922                }
923            }
924        }
925    }
926
927    protected void processDispatch(Command command) throws IOException {
928        MessageDispatch messageDispatch = (MessageDispatch) (command.isMessageDispatch() ? command : null);
929        try {
930            if (!stopping.get()) {
931                if (messageDispatch != null) {
932                    try {
933                        broker.preProcessDispatch(messageDispatch);
934                    } catch (RuntimeException convertToIO) {
935                        throw new IOException(convertToIO);
936                    }
937                }
938                dispatch(command);
939            }
940        } catch (IOException e) {
941            if (messageDispatch != null) {
942                TransmitCallback sub = messageDispatch.getTransmitCallback();
943                broker.postProcessDispatch(messageDispatch);
944                if (sub != null) {
945                    sub.onFailure();
946                }
947                messageDispatch = null;
948                throw e;
949            }
950        } finally {
951            if (messageDispatch != null) {
952                TransmitCallback sub = messageDispatch.getTransmitCallback();
953                broker.postProcessDispatch(messageDispatch);
954                if (sub != null) {
955                    sub.onSuccess();
956                }
957            }
958        }
959    }
960
961    @Override
962    public boolean iterate() {
963        try {
964            if (pendingStop || stopping.get()) {
965                if (dispatchStopped.compareAndSet(false, true)) {
966                    if (transportException.get() == null) {
967                        try {
968                            dispatch(new ShutdownInfo());
969                        } catch (Throwable ignore) {
970                        }
971                    }
972                    dispatchStoppedLatch.countDown();
973                }
974                return false;
975            }
976            if (!dispatchStopped.get()) {
977                Command command = null;
978                synchronized (dispatchQueue) {
979                    if (dispatchQueue.isEmpty()) {
980                        return false;
981                    }
982                    command = dispatchQueue.remove(0);
983                }
984                processDispatch(command);
985                return true;
986            }
987            return false;
988        } catch (IOException e) {
989            if (dispatchStopped.compareAndSet(false, true)) {
990                dispatchStoppedLatch.countDown();
991            }
992            serviceExceptionAsync(e);
993            return false;
994        }
995    }
996
997    /**
998     * Returns the statistics for this connection
999     */
1000    @Override
1001    public ConnectionStatistics getStatistics() {
1002        return statistics;
1003    }
1004
1005    public MessageAuthorizationPolicy getMessageAuthorizationPolicy() {
1006        return messageAuthorizationPolicy;
1007    }
1008
1009    public void setMessageAuthorizationPolicy(MessageAuthorizationPolicy messageAuthorizationPolicy) {
1010        this.messageAuthorizationPolicy = messageAuthorizationPolicy;
1011    }
1012
1013    @Override
1014    public boolean isManageable() {
1015        return manageable;
1016    }
1017
1018    @Override
1019    public void start() throws Exception {
1020        try {
1021            synchronized (this) {
1022                starting = true;
1023                if (taskRunnerFactory != null) {
1024                    taskRunner = taskRunnerFactory.createTaskRunner(this, "ActiveMQ Connection Dispatcher: "
1025                            + getRemoteAddress());
1026                } else {
1027                    taskRunner = null;
1028                }
1029                transport.start();
1030                active = true;
1031                BrokerInfo info = connector.getBrokerInfo().copy();
1032                if (connector.isUpdateClusterClients()) {
1033                    info.setPeerBrokerInfos(this.broker.getPeerBrokerInfos());
1034                } else {
1035                    info.setPeerBrokerInfos(null);
1036                }
1037                dispatchAsync(info);
1038
1039                connector.onStarted(this);
1040            }
1041        } catch (Exception e) {
1042            // Force clean up on an error starting up.
1043            pendingStop = true;
1044            throw e;
1045        } finally {
1046            // stop() can be called from within the above block,
1047            // but we want to be sure start() completes before
1048            // stop() runs, so queue the stop until right now:
1049            setStarting(false);
1050            if (isPendingStop()) {
1051                LOG.debug("Calling the delayed stop() after start() {}", this);
1052                stop();
1053            }
1054        }
1055    }
1056
1057    @Override
1058    public void stop() throws Exception {
1059        // do not stop task the task runner factories (taskRunnerFactory, stopTaskRunnerFactory)
1060        // as their lifecycle is handled elsewhere
1061
1062        stopAsync();
1063        while (!stopped.await(5, TimeUnit.SECONDS)) {
1064            LOG.info("The connection to '{}' is taking a long time to shutdown.", transport.getRemoteAddress());
1065        }
1066    }
1067
1068    public void delayedStop(final int waitTime, final String reason, Throwable cause) {
1069        if (waitTime > 0) {
1070            synchronized (this) {
1071                pendingStop = true;
1072                stopError = cause;
1073            }
1074            try {
1075                stopTaskRunnerFactory.execute(new Runnable() {
1076                    @Override
1077                    public void run() {
1078                        try {
1079                            Thread.sleep(waitTime);
1080                            stopAsync();
1081                            LOG.info("Stopping {} because {}", transport.getRemoteAddress(), reason);
1082                        } catch (InterruptedException e) {
1083                        }
1084                    }
1085                });
1086            } catch (Throwable t) {
1087                LOG.warn("Cannot create stopAsync. This exception will be ignored.", t);
1088            }
1089        }
1090    }
1091
1092    public void stopAsync() {
1093        // If we're in the middle of starting then go no further... for now.
1094        synchronized (this) {
1095            pendingStop = true;
1096            if (starting) {
1097                LOG.debug("stopAsync() called in the middle of start(). Delaying till start completes..");
1098                return;
1099            }
1100        }
1101        if (stopping.compareAndSet(false, true)) {
1102            // Let all the connection contexts know we are shutting down
1103            // so that in progress operations can notice and unblock.
1104            List<TransportConnectionState> connectionStates = listConnectionStates();
1105            for (TransportConnectionState cs : connectionStates) {
1106                ConnectionContext connectionContext = cs.getContext();
1107                if (connectionContext != null) {
1108                    connectionContext.getStopping().set(true);
1109                }
1110            }
1111            try {
1112                stopTaskRunnerFactory.execute(new Runnable() {
1113                    @Override
1114                    public void run() {
1115                        serviceLock.writeLock().lock();
1116                        try {
1117                            doStop();
1118                        } catch (Throwable e) {
1119                            LOG.debug("Error occurred while shutting down a connection {}", this, e);
1120                        } finally {
1121                            stopped.countDown();
1122                            serviceLock.writeLock().unlock();
1123                        }
1124                    }
1125                });
1126            } catch (Throwable t) {
1127                LOG.warn("Cannot create async transport stopper thread. This exception is ignored. Not waiting for stop to complete", t);
1128                stopped.countDown();
1129            }
1130        }
1131    }
1132
1133    @Override
1134    public String toString() {
1135        return "Transport Connection to: " + transport.getRemoteAddress();
1136    }
1137
1138    protected void doStop() throws Exception {
1139        LOG.debug("Stopping connection: {}", transport.getRemoteAddress());
1140        connector.onStopped(this);
1141        try {
1142            synchronized (this) {
1143                if (duplexBridge != null) {
1144                    duplexBridge.stop();
1145                }
1146            }
1147        } catch (Exception ignore) {
1148            LOG.trace("Exception caught stopping. This exception is ignored.", ignore);
1149        }
1150        try {
1151            transport.stop();
1152            LOG.debug("Stopped transport: {}", transport.getRemoteAddress());
1153        } catch (Exception e) {
1154            LOG.debug("Could not stop transport to {}. This exception is ignored.", transport.getRemoteAddress(), e);
1155        }
1156        if (taskRunner != null) {
1157            taskRunner.shutdown(1);
1158            taskRunner = null;
1159        }
1160        active = false;
1161        // Run the MessageDispatch callbacks so that message references get
1162        // cleaned up.
1163        synchronized (dispatchQueue) {
1164            for (Iterator<Command> iter = dispatchQueue.iterator(); iter.hasNext(); ) {
1165                Command command = iter.next();
1166                if (command.isMessageDispatch()) {
1167                    MessageDispatch md = (MessageDispatch) command;
1168                    TransmitCallback sub = md.getTransmitCallback();
1169                    broker.postProcessDispatch(md);
1170                    if (sub != null) {
1171                        sub.onFailure();
1172                    }
1173                }
1174            }
1175            dispatchQueue.clear();
1176        }
1177        //
1178        // Remove all logical connection associated with this connection
1179        // from the broker.
1180        if (!broker.isStopped()) {
1181            List<TransportConnectionState> connectionStates = listConnectionStates();
1182            connectionStates = listConnectionStates();
1183            for (TransportConnectionState cs : connectionStates) {
1184                cs.getContext().getStopping().set(true);
1185                try {
1186                    LOG.debug("Cleaning up connection resources: {}", getRemoteAddress());
1187                    processRemoveConnection(cs.getInfo().getConnectionId(), -1);
1188                } catch (Throwable ignore) {
1189                    ignore.printStackTrace();
1190                }
1191            }
1192        }
1193        LOG.debug("Connection Stopped: {}", getRemoteAddress());
1194    }
1195
1196    /**
1197     * @return Returns the blockedCandidate.
1198     */
1199    public boolean isBlockedCandidate() {
1200        return blockedCandidate;
1201    }
1202
1203    /**
1204     * @param blockedCandidate The blockedCandidate to set.
1205     */
1206    public void setBlockedCandidate(boolean blockedCandidate) {
1207        this.blockedCandidate = blockedCandidate;
1208    }
1209
1210    /**
1211     * @return Returns the markedCandidate.
1212     */
1213    public boolean isMarkedCandidate() {
1214        return markedCandidate;
1215    }
1216
1217    /**
1218     * @param markedCandidate The markedCandidate to set.
1219     */
1220    public void setMarkedCandidate(boolean markedCandidate) {
1221        this.markedCandidate = markedCandidate;
1222        if (!markedCandidate) {
1223            timeStamp = 0;
1224            blockedCandidate = false;
1225        }
1226    }
1227
1228    /**
1229     * @param slow The slow to set.
1230     */
1231    public void setSlow(boolean slow) {
1232        this.slow = slow;
1233    }
1234
1235    /**
1236     * @return true if the Connection is slow
1237     */
1238    @Override
1239    public boolean isSlow() {
1240        return slow;
1241    }
1242
1243    /**
1244     * @return true if the Connection is potentially blocked
1245     */
1246    public boolean isMarkedBlockedCandidate() {
1247        return markedCandidate;
1248    }
1249
1250    /**
1251     * Mark the Connection, so we can deem if it's collectable on the next sweep
1252     */
1253    public void doMark() {
1254        if (timeStamp == 0) {
1255            timeStamp = System.currentTimeMillis();
1256        }
1257    }
1258
1259    /**
1260     * @return if after being marked, the Connection is still writing
1261     */
1262    @Override
1263    public boolean isBlocked() {
1264        return blocked;
1265    }
1266
1267    /**
1268     * @return true if the Connection is connected
1269     */
1270    @Override
1271    public boolean isConnected() {
1272        return connected;
1273    }
1274
1275    /**
1276     * @param blocked The blocked to set.
1277     */
1278    public void setBlocked(boolean blocked) {
1279        this.blocked = blocked;
1280    }
1281
1282    /**
1283     * @param connected The connected to set.
1284     */
1285    public void setConnected(boolean connected) {
1286        this.connected = connected;
1287    }
1288
1289    /**
1290     * @return true if the Connection is active
1291     */
1292    @Override
1293    public boolean isActive() {
1294        return active;
1295    }
1296
1297    /**
1298     * @param active The active to set.
1299     */
1300    public void setActive(boolean active) {
1301        this.active = active;
1302    }
1303
1304    /**
1305     * @return true if the Connection is starting
1306     */
1307    public synchronized boolean isStarting() {
1308        return starting;
1309    }
1310
1311    @Override
1312    public synchronized boolean isNetworkConnection() {
1313        return networkConnection;
1314    }
1315
1316    @Override
1317    public boolean isFaultTolerantConnection() {
1318        return this.faultTolerantConnection;
1319    }
1320
1321    protected synchronized void setStarting(boolean starting) {
1322        this.starting = starting;
1323    }
1324
1325    /**
1326     * @return true if the Connection needs to stop
1327     */
1328    public synchronized boolean isPendingStop() {
1329        return pendingStop;
1330    }
1331
1332    protected synchronized void setPendingStop(boolean pendingStop) {
1333        this.pendingStop = pendingStop;
1334    }
1335
1336    @Override
1337    public Response processBrokerInfo(BrokerInfo info) {
1338        if (info.isSlaveBroker()) {
1339            LOG.error(" Slave Brokers are no longer supported - slave trying to attach is: {}", info.getBrokerName());
1340        } else if (info.isNetworkConnection() && info.isDuplexConnection()) {
1341            // so this TransportConnection is the rear end of a network bridge
1342            // We have been requested to create a two way pipe ...
1343            try {
1344                Properties properties = MarshallingSupport.stringToProperties(info.getNetworkProperties());
1345                Map<String, String> props = createMap(properties);
1346                NetworkBridgeConfiguration config = new NetworkBridgeConfiguration();
1347                IntrospectionSupport.setProperties(config, props, "");
1348                config.setBrokerName(broker.getBrokerName());
1349
1350                // check for existing duplex connection hanging about
1351
1352                // We first look if existing network connection already exists for the same broker Id and network connector name
1353                // It's possible in case of brief network fault to have this transport connector side of the connection always active
1354                // and the duplex network connector side wanting to open a new one
1355                // In this case, the old connection must be broken
1356                String duplexNetworkConnectorId = config.getName() + "@" + info.getBrokerId();
1357                CopyOnWriteArrayList<TransportConnection> connections = this.connector.getConnections();
1358                synchronized (connections) {
1359                    for (Iterator<TransportConnection> iter = connections.iterator(); iter.hasNext(); ) {
1360                        TransportConnection c = iter.next();
1361                        if ((c != this) && (duplexNetworkConnectorId.equals(c.getDuplexNetworkConnectorId()))) {
1362                            LOG.warn("Stopping an existing active duplex connection [{}] for network connector ({}).", c, duplexNetworkConnectorId);
1363                            c.stopAsync();
1364                            // better to wait for a bit rather than get connection id already in use and failure to start new bridge
1365                            c.getStopped().await(1, TimeUnit.SECONDS);
1366                        }
1367                    }
1368                    setDuplexNetworkConnectorId(duplexNetworkConnectorId);
1369                }
1370                Transport localTransport = NetworkBridgeFactory.createLocalTransport(broker);
1371                Transport remoteBridgeTransport = transport;
1372                if (! (remoteBridgeTransport instanceof ResponseCorrelator)) {
1373                    // the vm transport case is already wrapped
1374                    remoteBridgeTransport = new ResponseCorrelator(remoteBridgeTransport);
1375                }
1376                String duplexName = localTransport.toString();
1377                if (duplexName.contains("#")) {
1378                    duplexName = duplexName.substring(duplexName.lastIndexOf("#"));
1379                }
1380                MBeanNetworkListener listener = new MBeanNetworkListener(broker.getBrokerService(), config, broker.getBrokerService().createDuplexNetworkConnectorObjectName(duplexName));
1381                listener.setCreatedByDuplex(true);
1382                duplexBridge = NetworkBridgeFactory.createBridge(config, localTransport, remoteBridgeTransport, listener);
1383                duplexBridge.setBrokerService(broker.getBrokerService());
1384                // now turn duplex off this side
1385                info.setDuplexConnection(false);
1386                duplexBridge.setCreatedByDuplex(true);
1387                duplexBridge.duplexStart(this, brokerInfo, info);
1388                LOG.info("Started responder end of duplex bridge {}", duplexNetworkConnectorId);
1389                return null;
1390            } catch (TransportDisposedIOException e) {
1391                LOG.warn("Duplex bridge {} was stopped before it was correctly started.", duplexNetworkConnectorId);
1392                return null;
1393            } catch (Exception e) {
1394                LOG.error("Failed to create responder end of duplex network bridge {}", duplexNetworkConnectorId, e);
1395                return null;
1396            }
1397        }
1398        // We only expect to get one broker info command per connection
1399        if (this.brokerInfo != null) {
1400            LOG.warn("Unexpected extra broker info command received: {}", info);
1401        }
1402        this.brokerInfo = info;
1403        networkConnection = true;
1404        List<TransportConnectionState> connectionStates = listConnectionStates();
1405        for (TransportConnectionState cs : connectionStates) {
1406            cs.getContext().setNetworkConnection(true);
1407        }
1408        return null;
1409    }
1410
1411    @SuppressWarnings({"unchecked", "rawtypes"})
1412    private HashMap<String, String> createMap(Properties properties) {
1413        return new HashMap(properties);
1414    }
1415
1416    protected void dispatch(Command command) throws IOException {
1417        try {
1418            setMarkedCandidate(true);
1419            transport.oneway(command);
1420        } finally {
1421            setMarkedCandidate(false);
1422        }
1423    }
1424
1425    @Override
1426    public String getRemoteAddress() {
1427        return transport.getRemoteAddress();
1428    }
1429
1430    public Transport getTransport() {
1431        return transport;
1432    }
1433
1434    @Override
1435    public String getConnectionId() {
1436        List<TransportConnectionState> connectionStates = listConnectionStates();
1437        for (TransportConnectionState cs : connectionStates) {
1438            if (cs.getInfo().getClientId() != null) {
1439                return cs.getInfo().getClientId();
1440            }
1441            return cs.getInfo().getConnectionId().toString();
1442        }
1443        return null;
1444    }
1445
1446    @Override
1447    public void updateClient(ConnectionControl control) {
1448        if (isActive() && isBlocked() == false && isFaultTolerantConnection() && this.wireFormatInfo != null
1449                && this.wireFormatInfo.getVersion() >= 6) {
1450            dispatchAsync(control);
1451        }
1452    }
1453
1454    public ProducerBrokerExchange getProducerBrokerExchangeIfExists(ProducerInfo producerInfo){
1455        ProducerBrokerExchange result = null;
1456        if (producerInfo != null && producerInfo.getProducerId() != null){
1457            synchronized (producerExchanges){
1458                result = producerExchanges.get(producerInfo.getProducerId());
1459            }
1460        }
1461        return result;
1462    }
1463
1464    private ProducerBrokerExchange getProducerBrokerExchange(ProducerId id) throws IOException {
1465        ProducerBrokerExchange result = producerExchanges.get(id);
1466        if (result == null) {
1467            synchronized (producerExchanges) {
1468                result = new ProducerBrokerExchange();
1469                TransportConnectionState state = lookupConnectionState(id);
1470                context = state.getContext();
1471                result.setConnectionContext(context);
1472                if (context.isReconnect() || (context.isNetworkConnection() && connector.isAuditNetworkProducers())) {
1473                    result.setLastStoredSequenceId(broker.getBrokerService().getPersistenceAdapter().getLastProducerSequenceId(id));
1474                }
1475                SessionState ss = state.getSessionState(id.getParentId());
1476                if (ss != null) {
1477                    result.setProducerState(ss.getProducerState(id));
1478                    ProducerState producerState = ss.getProducerState(id);
1479                    if (producerState != null && producerState.getInfo() != null) {
1480                        ProducerInfo info = producerState.getInfo();
1481                        result.setMutable(info.getDestination() == null || info.getDestination().isComposite());
1482                    }
1483                }
1484                producerExchanges.put(id, result);
1485            }
1486        } else {
1487            context = result.getConnectionContext();
1488        }
1489        return result;
1490    }
1491
1492    private void removeProducerBrokerExchange(ProducerId id) {
1493        synchronized (producerExchanges) {
1494            producerExchanges.remove(id);
1495        }
1496    }
1497
1498    private ConsumerBrokerExchange getConsumerBrokerExchange(ConsumerId id) {
1499        ConsumerBrokerExchange result = consumerExchanges.get(id);
1500        return result;
1501    }
1502
1503    private ConsumerBrokerExchange addConsumerBrokerExchange(ConsumerId id) {
1504        ConsumerBrokerExchange result = consumerExchanges.get(id);
1505        if (result == null) {
1506            synchronized (consumerExchanges) {
1507                result = new ConsumerBrokerExchange();
1508                TransportConnectionState state = lookupConnectionState(id);
1509                context = state.getContext();
1510                result.setConnectionContext(context);
1511                SessionState ss = state.getSessionState(id.getParentId());
1512                if (ss != null) {
1513                    ConsumerState cs = ss.getConsumerState(id);
1514                    if (cs != null) {
1515                        ConsumerInfo info = cs.getInfo();
1516                        if (info != null) {
1517                            if (info.getDestination() != null && info.getDestination().isPattern()) {
1518                                result.setWildcard(true);
1519                            }
1520                        }
1521                    }
1522                }
1523                consumerExchanges.put(id, result);
1524            }
1525        }
1526        return result;
1527    }
1528
1529    private void removeConsumerBrokerExchange(ConsumerId id) {
1530        synchronized (consumerExchanges) {
1531            consumerExchanges.remove(id);
1532        }
1533    }
1534
1535    public int getProtocolVersion() {
1536        return protocolVersion.get();
1537    }
1538
1539    @Override
1540    public Response processControlCommand(ControlCommand command) throws Exception {
1541        return null;
1542    }
1543
1544    @Override
1545    public Response processMessageDispatch(MessageDispatch dispatch) throws Exception {
1546        return null;
1547    }
1548
1549    @Override
1550    public Response processConnectionControl(ConnectionControl control) throws Exception {
1551        if (control != null) {
1552            faultTolerantConnection = control.isFaultTolerant();
1553        }
1554        return null;
1555    }
1556
1557    @Override
1558    public Response processConnectionError(ConnectionError error) throws Exception {
1559        return null;
1560    }
1561
1562    @Override
1563    public Response processConsumerControl(ConsumerControl control) throws Exception {
1564        ConsumerBrokerExchange consumerExchange = getConsumerBrokerExchange(control.getConsumerId());
1565        broker.processConsumerControl(consumerExchange, control);
1566        return null;
1567    }
1568
1569    protected synchronized TransportConnectionState registerConnectionState(ConnectionId connectionId,
1570                                                                            TransportConnectionState state) {
1571        TransportConnectionState cs = null;
1572        if (!connectionStateRegister.isEmpty() && !connectionStateRegister.doesHandleMultipleConnectionStates()) {
1573            // swap implementations
1574            TransportConnectionStateRegister newRegister = new MapTransportConnectionStateRegister();
1575            newRegister.intialize(connectionStateRegister);
1576            connectionStateRegister = newRegister;
1577        }
1578        cs = connectionStateRegister.registerConnectionState(connectionId, state);
1579        return cs;
1580    }
1581
1582    protected synchronized TransportConnectionState unregisterConnectionState(ConnectionId connectionId) {
1583        return connectionStateRegister.unregisterConnectionState(connectionId);
1584    }
1585
1586    protected synchronized List<TransportConnectionState> listConnectionStates() {
1587        return connectionStateRegister.listConnectionStates();
1588    }
1589
1590    protected synchronized TransportConnectionState lookupConnectionState(String connectionId) {
1591        return connectionStateRegister.lookupConnectionState(connectionId);
1592    }
1593
1594    protected synchronized TransportConnectionState lookupConnectionState(ConsumerId id) {
1595        return connectionStateRegister.lookupConnectionState(id);
1596    }
1597
1598    protected synchronized TransportConnectionState lookupConnectionState(ProducerId id) {
1599        return connectionStateRegister.lookupConnectionState(id);
1600    }
1601
1602    protected synchronized TransportConnectionState lookupConnectionState(SessionId id) {
1603        return connectionStateRegister.lookupConnectionState(id);
1604    }
1605
1606    // public only for testing
1607    public synchronized TransportConnectionState lookupConnectionState(ConnectionId connectionId) {
1608        return connectionStateRegister.lookupConnectionState(connectionId);
1609    }
1610
1611    protected synchronized void setDuplexNetworkConnectorId(String duplexNetworkConnectorId) {
1612        this.duplexNetworkConnectorId = duplexNetworkConnectorId;
1613    }
1614
1615    protected synchronized String getDuplexNetworkConnectorId() {
1616        return this.duplexNetworkConnectorId;
1617    }
1618
1619    public boolean isStopping() {
1620        return stopping.get();
1621    }
1622
1623    protected CountDownLatch getStopped() {
1624        return stopped;
1625    }
1626
1627    private int getProducerCount(ConnectionId connectionId) {
1628        int result = 0;
1629        TransportConnectionState cs = lookupConnectionState(connectionId);
1630        if (cs != null) {
1631            for (SessionId sessionId : cs.getSessionIds()) {
1632                SessionState sessionState = cs.getSessionState(sessionId);
1633                if (sessionState != null) {
1634                    result += sessionState.getProducerIds().size();
1635                }
1636            }
1637        }
1638        return result;
1639    }
1640
1641    private int getConsumerCount(ConnectionId connectionId) {
1642        int result = 0;
1643        TransportConnectionState cs = lookupConnectionState(connectionId);
1644        if (cs != null) {
1645            for (SessionId sessionId : cs.getSessionIds()) {
1646                SessionState sessionState = cs.getSessionState(sessionId);
1647                if (sessionState != null) {
1648                    result += sessionState.getConsumerIds().size();
1649                }
1650            }
1651        }
1652        return result;
1653    }
1654
1655    public WireFormatInfo getRemoteWireFormatInfo() {
1656        return wireFormatInfo;
1657    }
1658}