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