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