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