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