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;
018
019import java.io.IOException;
020import java.net.URI;
021import java.net.URISyntaxException;
022import java.util.ArrayList;
023import java.util.HashMap;
024import java.util.Iterator;
025import java.util.List;
026import java.util.Map;
027import java.util.concurrent.ConcurrentHashMap;
028import java.util.concurrent.ConcurrentMap;
029import java.util.concurrent.CopyOnWriteArrayList;
030import java.util.concurrent.CountDownLatch;
031import java.util.concurrent.LinkedBlockingQueue;
032import java.util.concurrent.RejectedExecutionHandler;
033import java.util.concurrent.ThreadFactory;
034import java.util.concurrent.ThreadPoolExecutor;
035import java.util.concurrent.TimeUnit;
036import java.util.concurrent.atomic.AtomicBoolean;
037import java.util.concurrent.atomic.AtomicInteger;
038import java.util.concurrent.atomic.AtomicLong;
039
040import javax.jms.Connection;
041import javax.jms.ConnectionConsumer;
042import javax.jms.ConnectionMetaData;
043import javax.jms.Destination;
044import javax.jms.ExceptionListener;
045import javax.jms.IllegalStateException;
046import javax.jms.InvalidDestinationException;
047import javax.jms.JMSException;
048import javax.jms.Queue;
049import javax.jms.QueueConnection;
050import javax.jms.QueueSession;
051import javax.jms.ServerSessionPool;
052import javax.jms.Session;
053import javax.jms.Topic;
054import javax.jms.TopicConnection;
055import javax.jms.TopicSession;
056import javax.jms.XAConnection;
057
058import org.apache.activemq.advisory.DestinationSource;
059import org.apache.activemq.blob.BlobTransferPolicy;
060import org.apache.activemq.broker.region.policy.RedeliveryPolicyMap;
061import org.apache.activemq.command.ActiveMQDestination;
062import org.apache.activemq.command.ActiveMQMessage;
063import org.apache.activemq.command.ActiveMQTempDestination;
064import org.apache.activemq.command.ActiveMQTempQueue;
065import org.apache.activemq.command.ActiveMQTempTopic;
066import org.apache.activemq.command.BrokerInfo;
067import org.apache.activemq.command.Command;
068import org.apache.activemq.command.CommandTypes;
069import org.apache.activemq.command.ConnectionControl;
070import org.apache.activemq.command.ConnectionError;
071import org.apache.activemq.command.ConnectionId;
072import org.apache.activemq.command.ConnectionInfo;
073import org.apache.activemq.command.ConsumerControl;
074import org.apache.activemq.command.ConsumerId;
075import org.apache.activemq.command.ConsumerInfo;
076import org.apache.activemq.command.ControlCommand;
077import org.apache.activemq.command.DestinationInfo;
078import org.apache.activemq.command.ExceptionResponse;
079import org.apache.activemq.command.Message;
080import org.apache.activemq.command.MessageDispatch;
081import org.apache.activemq.command.MessageId;
082import org.apache.activemq.command.ProducerAck;
083import org.apache.activemq.command.ProducerId;
084import org.apache.activemq.command.RemoveInfo;
085import org.apache.activemq.command.RemoveSubscriptionInfo;
086import org.apache.activemq.command.Response;
087import org.apache.activemq.command.SessionId;
088import org.apache.activemq.command.ShutdownInfo;
089import org.apache.activemq.command.WireFormatInfo;
090import org.apache.activemq.management.JMSConnectionStatsImpl;
091import org.apache.activemq.management.JMSStatsImpl;
092import org.apache.activemq.management.StatsCapable;
093import org.apache.activemq.management.StatsImpl;
094import org.apache.activemq.state.CommandVisitorAdapter;
095import org.apache.activemq.thread.Scheduler;
096import org.apache.activemq.thread.TaskRunnerFactory;
097import org.apache.activemq.transport.FutureResponse;
098import org.apache.activemq.transport.RequestTimedOutIOException;
099import org.apache.activemq.transport.ResponseCallback;
100import org.apache.activemq.transport.Transport;
101import org.apache.activemq.transport.TransportListener;
102import org.apache.activemq.transport.failover.FailoverTransport;
103import org.apache.activemq.util.IdGenerator;
104import org.apache.activemq.util.IntrospectionSupport;
105import org.apache.activemq.util.JMSExceptionSupport;
106import org.apache.activemq.util.LongSequenceGenerator;
107import org.apache.activemq.util.ServiceSupport;
108import org.apache.activemq.util.ThreadPoolUtils;
109import org.slf4j.Logger;
110import org.slf4j.LoggerFactory;
111
112public class ActiveMQConnection implements Connection, TopicConnection, QueueConnection, StatsCapable, Closeable, TransportListener, EnhancedConnection {
113
114    public static final String DEFAULT_USER = ActiveMQConnectionFactory.DEFAULT_USER;
115    public static final String DEFAULT_PASSWORD = ActiveMQConnectionFactory.DEFAULT_PASSWORD;
116    public static final String DEFAULT_BROKER_URL = ActiveMQConnectionFactory.DEFAULT_BROKER_URL;
117    public static int DEFAULT_THREAD_POOL_SIZE = 1000;
118
119    private static final Logger LOG = LoggerFactory.getLogger(ActiveMQConnection.class);
120
121    public final ConcurrentMap<ActiveMQTempDestination, ActiveMQTempDestination> activeTempDestinations = new ConcurrentHashMap<>();
122
123    protected boolean dispatchAsync=true;
124    protected boolean alwaysSessionAsync = true;
125
126    private TaskRunnerFactory sessionTaskRunner;
127    private final ThreadPoolExecutor executor;
128
129    // Connection state variables
130    private final ConnectionInfo info;
131    private ExceptionListener exceptionListener;
132    private ClientInternalExceptionListener clientInternalExceptionListener;
133    private boolean clientIDSet;
134    private boolean isConnectionInfoSentToBroker;
135    private boolean userSpecifiedClientID;
136
137    // Configuration options variables
138    private ActiveMQPrefetchPolicy prefetchPolicy = new ActiveMQPrefetchPolicy();
139    private BlobTransferPolicy blobTransferPolicy;
140    private RedeliveryPolicyMap redeliveryPolicyMap;
141    private MessageTransformer transformer;
142
143    private boolean disableTimeStampsByDefault;
144    private boolean optimizedMessageDispatch = true;
145    private boolean copyMessageOnSend = true;
146    private boolean useCompression;
147    private boolean objectMessageSerializationDefered;
148    private boolean useAsyncSend;
149    private boolean optimizeAcknowledge;
150    private long optimizeAcknowledgeTimeOut = 0;
151    private long optimizedAckScheduledAckInterval = 0;
152    private boolean nestedMapAndListEnabled = true;
153    private boolean useRetroactiveConsumer;
154    private boolean exclusiveConsumer;
155    private boolean alwaysSyncSend;
156    private int closeTimeout = 15000;
157    private boolean watchTopicAdvisories = true;
158    private long warnAboutUnstartedConnectionTimeout = 500L;
159    private int sendTimeout =0;
160    private boolean sendAcksAsync=true;
161    private boolean checkForDuplicates = true;
162    private boolean queueOnlyConnection = false;
163    private boolean consumerExpiryCheckEnabled = true;
164
165    private final Transport transport;
166    private final IdGenerator clientIdGenerator;
167    private final JMSStatsImpl factoryStats;
168    private final JMSConnectionStatsImpl stats;
169
170    private final AtomicBoolean started = new AtomicBoolean(false);
171    private final AtomicBoolean closing = new AtomicBoolean(false);
172    private final AtomicBoolean closed = new AtomicBoolean(false);
173    private final AtomicBoolean transportFailed = new AtomicBoolean(false);
174    private final CopyOnWriteArrayList<ActiveMQSession> sessions = new CopyOnWriteArrayList<>();
175    private final CopyOnWriteArrayList<ActiveMQConnectionConsumer> connectionConsumers = new CopyOnWriteArrayList<>();
176    private final CopyOnWriteArrayList<TransportListener> transportListeners = new CopyOnWriteArrayList<>();
177
178    // Maps ConsumerIds to ActiveMQConsumer objects
179    private final ConcurrentMap<ConsumerId, ActiveMQDispatcher> dispatchers = new ConcurrentHashMap<>();
180    private final ConcurrentMap<ProducerId, ActiveMQMessageProducer> producers = new ConcurrentHashMap<>();
181    private final LongSequenceGenerator sessionIdGenerator = new LongSequenceGenerator();
182    private final SessionId connectionSessionId;
183    private final LongSequenceGenerator consumerIdGenerator = new LongSequenceGenerator();
184    private final LongSequenceGenerator tempDestinationIdGenerator = new LongSequenceGenerator();
185    private final LongSequenceGenerator localTransactionIdGenerator = new LongSequenceGenerator();
186
187    private AdvisoryConsumer advisoryConsumer;
188    private final CountDownLatch brokerInfoReceived = new CountDownLatch(1);
189    private BrokerInfo brokerInfo;
190    private IOException firstFailureError;
191    private int producerWindowSize = ActiveMQConnectionFactory.DEFAULT_PRODUCER_WINDOW_SIZE;
192
193    // Assume that protocol is the latest. Change to the actual protocol
194    // version when a WireFormatInfo is received.
195    private final AtomicInteger protocolVersion = new AtomicInteger(CommandTypes.PROTOCOL_VERSION);
196    private final AtomicLong maxFrameSize = new AtomicLong(Long.MAX_VALUE);
197    private final long timeCreated;
198    private final ConnectionAudit connectionAudit = new ConnectionAudit();
199    private DestinationSource destinationSource;
200    private final Object ensureConnectionInfoSentMutex = new Object();
201    private boolean useDedicatedTaskRunner;
202    protected AtomicInteger transportInterruptionProcessingComplete = new AtomicInteger(0);
203    private long consumerFailoverRedeliveryWaitPeriod;
204    private volatile Scheduler scheduler;
205    private final Object schedulerLock = new Object();
206    private boolean messagePrioritySupported = false;
207    private boolean transactedIndividualAck = false;
208    private boolean nonBlockingRedelivery = false;
209    private boolean rmIdFromConnectionId = false;
210
211    private int maxThreadPoolSize = DEFAULT_THREAD_POOL_SIZE;
212    private RejectedExecutionHandler rejectedTaskHandler = null;
213
214    private List<String> trustedPackages = new ArrayList<>();
215    private boolean trustAllPackages = false;
216    private int connectResponseTimeout;
217
218    /**
219     * Construct an <code>ActiveMQConnection</code>
220     *
221     * @param transport
222     * @param factoryStats
223     * @throws Exception
224     */
225    protected ActiveMQConnection(final Transport transport, IdGenerator clientIdGenerator, IdGenerator connectionIdGenerator, JMSStatsImpl factoryStats) throws Exception {
226
227        this.transport = transport;
228        this.clientIdGenerator = clientIdGenerator;
229        this.factoryStats = factoryStats;
230
231        // Configure a single threaded executor who's core thread can timeout if
232        // idle
233        executor = new ThreadPoolExecutor(1, 1, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new ThreadFactory() {
234            @Override
235            public Thread newThread(Runnable r) {
236                Thread thread = new Thread(r, "ActiveMQ Connection Executor: " + transport);
237                //Don't make these daemon threads - see https://issues.apache.org/jira/browse/AMQ-796
238                //thread.setDaemon(true);
239                return thread;
240            }
241        });
242        // asyncConnectionThread.allowCoreThreadTimeOut(true);
243        String uniqueId = connectionIdGenerator.generateId();
244        this.info = new ConnectionInfo(new ConnectionId(uniqueId));
245        this.info.setManageable(true);
246        this.info.setFaultTolerant(transport.isFaultTolerant());
247        this.connectionSessionId = new SessionId(info.getConnectionId(), -1);
248
249        this.transport.setTransportListener(this);
250
251        this.stats = new JMSConnectionStatsImpl(sessions, this instanceof XAConnection);
252        this.factoryStats.addConnection(this);
253        this.timeCreated = System.currentTimeMillis();
254        this.connectionAudit.setCheckForDuplicates(transport.isFaultTolerant());
255    }
256
257    protected void setUserName(String userName) {
258        this.info.setUserName(userName);
259    }
260
261    protected void setPassword(String password) {
262        this.info.setPassword(password);
263    }
264
265    /**
266     * A static helper method to create a new connection
267     *
268     * @return an ActiveMQConnection
269     * @throws JMSException
270     */
271    public static ActiveMQConnection makeConnection() throws JMSException {
272        ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory();
273        return (ActiveMQConnection)factory.createConnection();
274    }
275
276    /**
277     * A static helper method to create a new connection
278     *
279     * @param uri
280     * @return and ActiveMQConnection
281     * @throws JMSException
282     */
283    public static ActiveMQConnection makeConnection(String uri) throws JMSException, URISyntaxException {
284        ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(uri);
285        return (ActiveMQConnection)factory.createConnection();
286    }
287
288    /**
289     * A static helper method to create a new connection
290     *
291     * @param user
292     * @param password
293     * @param uri
294     * @return an ActiveMQConnection
295     * @throws JMSException
296     */
297    public static ActiveMQConnection makeConnection(String user, String password, String uri) throws JMSException, URISyntaxException {
298        ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(user, password, new URI(uri));
299        return (ActiveMQConnection)factory.createConnection();
300    }
301
302    /**
303     * @return a number unique for this connection
304     */
305    public JMSConnectionStatsImpl getConnectionStats() {
306        return stats;
307    }
308
309    /**
310     * Creates a <CODE>Session</CODE> object.
311     *
312     * @param transacted indicates whether the session is transacted
313     * @param acknowledgeMode indicates whether the consumer or the client will
314     *                acknowledge any messages it receives; ignored if the
315     *                session is transacted. Legal values are
316     *                <code>Session.AUTO_ACKNOWLEDGE</code>,
317     *                <code>Session.CLIENT_ACKNOWLEDGE</code>, and
318     *                <code>Session.DUPS_OK_ACKNOWLEDGE</code>.
319     * @return a newly created session
320     * @throws JMSException if the <CODE>Connection</CODE> object fails to
321     *                 create a session due to some internal error or lack of
322     *                 support for the specific transaction and acknowledgement
323     *                 mode.
324     * @see Session#AUTO_ACKNOWLEDGE
325     * @see Session#CLIENT_ACKNOWLEDGE
326     * @see Session#DUPS_OK_ACKNOWLEDGE
327     * @since 1.1
328     */
329    @Override
330    public Session createSession(boolean transacted, int acknowledgeMode) throws JMSException {
331        checkClosedOrFailed();
332        ensureConnectionInfoSent();
333        if (!transacted) {
334            if (acknowledgeMode == Session.SESSION_TRANSACTED) {
335                throw new JMSException("acknowledgeMode SESSION_TRANSACTED cannot be used for an non-transacted Session");
336            } else if (acknowledgeMode < Session.SESSION_TRANSACTED || acknowledgeMode > ActiveMQSession.MAX_ACK_CONSTANT) {
337                throw new JMSException("invalid acknowledgeMode: " + acknowledgeMode + ". Valid values are Session.AUTO_ACKNOWLEDGE (1), " +
338                        "Session.CLIENT_ACKNOWLEDGE (2), Session.DUPS_OK_ACKNOWLEDGE (3), ActiveMQSession.INDIVIDUAL_ACKNOWLEDGE (4) or for transacted sessions Session.SESSION_TRANSACTED (0)");
339            }
340        }
341        return new ActiveMQSession(this, getNextSessionId(), transacted ? Session.SESSION_TRANSACTED : acknowledgeMode, isDispatchAsync(), isAlwaysSessionAsync());
342    }
343
344    /**
345     * @return sessionId
346     */
347    protected SessionId getNextSessionId() {
348        return new SessionId(info.getConnectionId(), sessionIdGenerator.getNextSequenceId());
349    }
350
351    /**
352     * Gets the client identifier for this connection.
353     * <P>
354     * This value is specific to the JMS provider. It is either preconfigured by
355     * an administrator in a <CODE> ConnectionFactory</CODE> object or assigned
356     * dynamically by the application by calling the <code>setClientID</code>
357     * method.
358     *
359     * @return the unique client identifier
360     * @throws JMSException if the JMS provider fails to return the client ID
361     *                 for this connection due to some internal error.
362     */
363    @Override
364    public String getClientID() throws JMSException {
365        checkClosedOrFailed();
366        return this.info.getClientId();
367    }
368
369    /**
370     * Sets the client identifier for this connection.
371     * <P>
372     * The preferred way to assign a JMS client's client identifier is for it to
373     * be configured in a client-specific <CODE>ConnectionFactory</CODE>
374     * object and transparently assigned to the <CODE>Connection</CODE> object
375     * it creates.
376     * <P>
377     * Alternatively, a client can set a connection's client identifier using a
378     * provider-specific value. The facility to set a connection's client
379     * identifier explicitly is not a mechanism for overriding the identifier
380     * that has been administratively configured. It is provided for the case
381     * where no administratively specified identifier exists. If one does exist,
382     * an attempt to change it by setting it must throw an
383     * <CODE>IllegalStateException</CODE>. If a client sets the client
384     * identifier explicitly, it must do so immediately after it creates the
385     * connection and before any other action on the connection is taken. After
386     * this point, setting the client identifier is a programming error that
387     * should throw an <CODE>IllegalStateException</CODE>.
388     * <P>
389     * The purpose of the client identifier is to associate a connection and its
390     * objects with a state maintained on behalf of the client by a provider.
391     * The only such state identified by the JMS API is that required to support
392     * durable subscriptions.
393     * <P>
394     * If another connection with the same <code>clientID</code> is already
395     * running when this method is called, the JMS provider should detect the
396     * duplicate ID and throw an <CODE>InvalidClientIDException</CODE>.
397     *
398     * @param newClientID the unique client identifier
399     * @throws JMSException if the JMS provider fails to set the client ID for
400     *                 this connection due to some internal error.
401     * @throws javax.jms.InvalidClientIDException if the JMS client specifies an
402     *                 invalid or duplicate client ID.
403     * @throws javax.jms.IllegalStateException if the JMS client attempts to set
404     *                 a connection's client ID at the wrong time or when it has
405     *                 been administratively configured.
406     */
407    @Override
408    public void setClientID(String newClientID) throws JMSException {
409        checkClosedOrFailed();
410
411        if (this.clientIDSet) {
412            throw new IllegalStateException("The clientID has already been set");
413        }
414
415        if (this.isConnectionInfoSentToBroker) {
416            throw new IllegalStateException("Setting clientID on a used Connection is not allowed");
417        }
418
419        this.info.setClientId(newClientID);
420        this.userSpecifiedClientID = true;
421        ensureConnectionInfoSent();
422    }
423
424    /**
425     * Sets the default client id that the connection will use if explicitly not
426     * set with the setClientId() call.
427     */
428    public void setDefaultClientID(String clientID) throws JMSException {
429        this.info.setClientId(clientID);
430        this.userSpecifiedClientID = true;
431    }
432
433    /**
434     * Gets the metadata for this connection.
435     *
436     * @return the connection metadata
437     * @throws JMSException if the JMS provider fails to get the connection
438     *                 metadata for this connection.
439     * @see javax.jms.ConnectionMetaData
440     */
441    @Override
442    public ConnectionMetaData getMetaData() throws JMSException {
443        checkClosedOrFailed();
444        return ActiveMQConnectionMetaData.INSTANCE;
445    }
446
447    /**
448     * Gets the <CODE>ExceptionListener</CODE> object for this connection. Not
449     * every <CODE>Connection</CODE> has an <CODE>ExceptionListener</CODE>
450     * associated with it.
451     *
452     * @return the <CODE>ExceptionListener</CODE> for this connection, or
453     *         null, if no <CODE>ExceptionListener</CODE> is associated with
454     *         this connection.
455     * @throws JMSException if the JMS provider fails to get the
456     *                 <CODE>ExceptionListener</CODE> for this connection.
457     * @see javax.jms.Connection#setExceptionListener(ExceptionListener)
458     */
459    @Override
460    public ExceptionListener getExceptionListener() throws JMSException {
461        checkClosedOrFailed();
462        return this.exceptionListener;
463    }
464
465    /**
466     * Sets an exception listener for this connection.
467     * <P>
468     * If a JMS provider detects a serious problem with a connection, it informs
469     * the connection's <CODE> ExceptionListener</CODE>, if one has been
470     * registered. It does this by calling the listener's <CODE>onException
471     * </CODE>
472     * method, passing it a <CODE>JMSException</CODE> object describing the
473     * problem.
474     * <P>
475     * An exception listener allows a client to be notified of a problem
476     * asynchronously. Some connections only consume messages, so they would
477     * have no other way to learn their connection has failed.
478     * <P>
479     * A connection serializes execution of its <CODE>ExceptionListener</CODE>.
480     * <P>
481     * A JMS provider should attempt to resolve connection problems itself
482     * before it notifies the client of them.
483     *
484     * @param listener the exception listener
485     * @throws JMSException if the JMS provider fails to set the exception
486     *                 listener for this connection.
487     */
488    @Override
489    public void setExceptionListener(ExceptionListener listener) throws JMSException {
490        checkClosedOrFailed();
491        this.exceptionListener = listener;
492    }
493
494    /**
495     * Gets the <code>ClientInternalExceptionListener</code> object for this connection.
496     * Not every <CODE>ActiveMQConnectionn</CODE> has a <CODE>ClientInternalExceptionListener</CODE>
497     * associated with it.
498     *
499     * @return the listener or <code>null</code> if no listener is registered with the connection.
500     */
501    public ClientInternalExceptionListener getClientInternalExceptionListener() {
502        return clientInternalExceptionListener;
503    }
504
505    /**
506     * Sets a client internal exception listener for this connection.
507     * The connection will notify the listener, if one has been registered, of exceptions thrown by container components
508     * (e.g. an EJB container in case of Message Driven Beans) during asynchronous processing of a message.
509     * It does this by calling the listener's <code>onException()</code> method passing it a <code>Throwable</code>
510     * describing the problem.
511     *
512     * @param listener the exception listener
513     */
514    public void setClientInternalExceptionListener(ClientInternalExceptionListener listener) {
515        this.clientInternalExceptionListener = listener;
516    }
517
518    /**
519     * Starts (or restarts) a connection's delivery of incoming messages. A call
520     * to <CODE>start</CODE> on a connection that has already been started is
521     * ignored.
522     *
523     * @throws JMSException if the JMS provider fails to start message delivery
524     *                 due to some internal error.
525     * @see javax.jms.Connection#stop()
526     */
527    @Override
528    public void start() throws JMSException {
529        checkClosedOrFailed();
530        ensureConnectionInfoSent();
531        if (started.compareAndSet(false, true)) {
532            for (Iterator<ActiveMQSession> i = sessions.iterator(); i.hasNext();) {
533                ActiveMQSession session = i.next();
534                session.start();
535            }
536        }
537    }
538
539    /**
540     * Temporarily stops a connection's delivery of incoming messages. Delivery
541     * can be restarted using the connection's <CODE>start</CODE> method. When
542     * the connection is stopped, delivery to all the connection's message
543     * consumers is inhibited: synchronous receives block, and messages are not
544     * delivered to message listeners.
545     * <P>
546     * This call blocks until receives and/or message listeners in progress have
547     * completed.
548     * <P>
549     * Stopping a connection has no effect on its ability to send messages. A
550     * call to <CODE>stop</CODE> on a connection that has already been stopped
551     * is ignored.
552     * <P>
553     * A call to <CODE>stop</CODE> must not return until delivery of messages
554     * has paused. This means that a client can rely on the fact that none of
555     * its message listeners will be called and that all threads of control
556     * waiting for <CODE>receive</CODE> calls to return will not return with a
557     * message until the connection is restarted. The receive timers for a
558     * stopped connection continue to advance, so receives may time out while
559     * the connection is stopped.
560     * <P>
561     * If message listeners are running when <CODE>stop</CODE> is invoked, the
562     * <CODE>stop</CODE> call must wait until all of them have returned before
563     * it may return. While these message listeners are completing, they must
564     * have the full services of the connection available to them.
565     *
566     * @throws JMSException if the JMS provider fails to stop message delivery
567     *                 due to some internal error.
568     * @see javax.jms.Connection#start()
569     */
570    @Override
571    public void stop() throws JMSException {
572        doStop(true);
573    }
574
575    /**
576     * @see #stop()
577     * @param checkClosed <tt>true</tt> to check for already closed and throw {@link java.lang.IllegalStateException} if already closed,
578     *                    <tt>false</tt> to skip this check
579     * @throws JMSException if the JMS provider fails to stop message delivery due to some internal error.
580     */
581    void doStop(boolean checkClosed) throws JMSException {
582        if (checkClosed) {
583            checkClosedOrFailed();
584        }
585        if (started.compareAndSet(true, false)) {
586            synchronized(sessions) {
587                for (Iterator<ActiveMQSession> i = sessions.iterator(); i.hasNext();) {
588                    ActiveMQSession s = i.next();
589                    s.stop();
590                }
591            }
592        }
593    }
594
595    /**
596     * Closes the connection.
597     * <P>
598     * Since a provider typically allocates significant resources outside the
599     * JVM on behalf of a connection, clients should close these resources when
600     * they are not needed. Relying on garbage collection to eventually reclaim
601     * these resources may not be timely enough.
602     * <P>
603     * There is no need to close the sessions, producers, and consumers of a
604     * closed connection.
605     * <P>
606     * Closing a connection causes all temporary destinations to be deleted.
607     * <P>
608     * When this method is invoked, it should not return until message
609     * processing has been shut down in an orderly fashion. This means that all
610     * message listeners that may have been running have returned, and that all
611     * pending receives have returned. A close terminates all pending message
612     * receives on the connection's sessions' consumers. The receives may return
613     * with a message or with null, depending on whether there was a message
614     * available at the time of the close. If one or more of the connection's
615     * sessions' message listeners is processing a message at the time when
616     * connection <CODE>close</CODE> is invoked, all the facilities of the
617     * connection and its sessions must remain available to those listeners
618     * until they return control to the JMS provider.
619     * <P>
620     * Closing a connection causes any of its sessions' transactions in progress
621     * to be rolled back. In the case where a session's work is coordinated by
622     * an external transaction manager, a session's <CODE>commit</CODE> and
623     * <CODE> rollback</CODE> methods are not used and the result of a closed
624     * session's work is determined later by the transaction manager. Closing a
625     * connection does NOT force an acknowledgment of client-acknowledged
626     * sessions.
627     * <P>
628     * Invoking the <CODE>acknowledge</CODE> method of a received message from
629     * a closed connection's session must throw an
630     * <CODE>IllegalStateException</CODE>. Closing a closed connection must
631     * NOT throw an exception.
632     *
633     * @throws JMSException if the JMS provider fails to close the connection
634     *                 due to some internal error. For example, a failure to
635     *                 release resources or to close a socket connection can
636     *                 cause this exception to be thrown.
637     */
638    @Override
639    public void close() throws JMSException {
640        try {
641            // If we were running, lets stop first.
642            if (!closed.get() && !transportFailed.get()) {
643                // do not fail if already closed as according to JMS spec we must not
644                // throw exception if already closed
645                doStop(false);
646            }
647
648            synchronized (this) {
649                if (!closed.get()) {
650                    closing.set(true);
651
652                    if (destinationSource != null) {
653                        destinationSource.stop();
654                        destinationSource = null;
655                    }
656                    if (advisoryConsumer != null) {
657                        advisoryConsumer.dispose();
658                        advisoryConsumer = null;
659                    }
660
661                    Scheduler scheduler = this.scheduler;
662                    if (scheduler != null) {
663                        try {
664                            scheduler.stop();
665                        } catch (Exception e) {
666                            JMSException ex =  JMSExceptionSupport.create(e);
667                            throw ex;
668                        }
669                    }
670
671                    long lastDeliveredSequenceId = -1;
672                    for (Iterator<ActiveMQSession> i = this.sessions.iterator(); i.hasNext();) {
673                        ActiveMQSession s = i.next();
674                        s.dispose();
675                        lastDeliveredSequenceId = Math.max(lastDeliveredSequenceId, s.getLastDeliveredSequenceId());
676                    }
677                    for (Iterator<ActiveMQConnectionConsumer> i = this.connectionConsumers.iterator(); i.hasNext();) {
678                        ActiveMQConnectionConsumer c = i.next();
679                        c.dispose();
680                    }
681
682                    this.activeTempDestinations.clear();
683
684                    try {
685                        if (isConnectionInfoSentToBroker) {
686                            // If we announced ourselves to the broker.. Try to let the broker
687                            // know that the connection is being shutdown.
688                            RemoveInfo removeCommand = info.createRemoveCommand();
689                            removeCommand.setLastDeliveredSequenceId(lastDeliveredSequenceId);
690                            try {
691                                syncSendPacket(removeCommand, closeTimeout);
692                            } catch (JMSException e) {
693                                if (e.getCause() instanceof RequestTimedOutIOException) {
694                                    // expected
695                                } else {
696                                    throw e;
697                                }
698                            }
699                            doAsyncSendPacket(new ShutdownInfo());
700                        }
701                    } finally { // release anyway even if previous communication fails
702                        started.set(false);
703
704                        // TODO if we move the TaskRunnerFactory to the connection
705                        // factory
706                        // then we may need to call
707                        // factory.onConnectionClose(this);
708                        if (sessionTaskRunner != null) {
709                            sessionTaskRunner.shutdown();
710                        }
711                        closed.set(true);
712                        closing.set(false);
713                    }
714                }
715            }
716        } finally {
717            try {
718                if (executor != null) {
719                    ThreadPoolUtils.shutdown(executor);
720                }
721            } catch (Throwable e) {
722                LOG.warn("Error shutting down thread pool: " + executor + ". This exception will be ignored.", e);
723            }
724
725            ServiceSupport.dispose(this.transport);
726
727            factoryStats.removeConnection(this);
728        }
729    }
730
731    /**
732     * Tells the broker to terminate its VM. This can be used to cleanly
733     * terminate a broker running in a standalone java process. Server must have
734     * property enable.vm.shutdown=true defined to allow this to work.
735     */
736    // TODO : org.apache.activemq.message.BrokerAdminCommand not yet
737    // implemented.
738    /*
739     * public void terminateBrokerVM() throws JMSException { BrokerAdminCommand
740     * command = new BrokerAdminCommand();
741     * command.setCommand(BrokerAdminCommand.SHUTDOWN_SERVER_VM);
742     * asyncSendPacket(command); }
743     */
744
745    /**
746     * Create a durable connection consumer for this connection (optional
747     * operation). This is an expert facility not used by regular JMS clients.
748     *
749     * @param topic topic to access
750     * @param subscriptionName durable subscription name
751     * @param messageSelector only messages with properties matching the message
752     *                selector expression are delivered. A value of null or an
753     *                empty string indicates that there is no message selector
754     *                for the message consumer.
755     * @param sessionPool the server session pool to associate with this durable
756     *                connection consumer
757     * @param maxMessages the maximum number of messages that can be assigned to
758     *                a server session at one time
759     * @return the durable connection consumer
760     * @throws JMSException if the <CODE>Connection</CODE> object fails to
761     *                 create a connection consumer due to some internal error
762     *                 or invalid arguments for <CODE>sessionPool</CODE> and
763     *                 <CODE>messageSelector</CODE>.
764     * @throws javax.jms.InvalidDestinationException if an invalid destination
765     *                 is specified.
766     * @throws javax.jms.InvalidSelectorException if the message selector is
767     *                 invalid.
768     * @see javax.jms.ConnectionConsumer
769     * @since 1.1
770     */
771    @Override
772    public ConnectionConsumer createDurableConnectionConsumer(Topic topic, String subscriptionName, String messageSelector, ServerSessionPool sessionPool, int maxMessages)
773        throws JMSException {
774        return this.createDurableConnectionConsumer(topic, subscriptionName, messageSelector, sessionPool, maxMessages, false);
775    }
776
777    /**
778     * Create a durable connection consumer for this connection (optional
779     * operation). This is an expert facility not used by regular JMS clients.
780     *
781     * @param topic topic to access
782     * @param subscriptionName durable subscription name
783     * @param messageSelector only messages with properties matching the message
784     *                selector expression are delivered. A value of null or an
785     *                empty string indicates that there is no message selector
786     *                for the message consumer.
787     * @param sessionPool the server session pool to associate with this durable
788     *                connection consumer
789     * @param maxMessages the maximum number of messages that can be assigned to
790     *                a server session at one time
791     * @param noLocal set true if you want to filter out messages published
792     *                locally
793     * @return the durable connection consumer
794     * @throws JMSException if the <CODE>Connection</CODE> object fails to
795     *                 create a connection consumer due to some internal error
796     *                 or invalid arguments for <CODE>sessionPool</CODE> and
797     *                 <CODE>messageSelector</CODE>.
798     * @throws javax.jms.InvalidDestinationException if an invalid destination
799     *                 is specified.
800     * @throws javax.jms.InvalidSelectorException if the message selector is
801     *                 invalid.
802     * @see javax.jms.ConnectionConsumer
803     * @since 1.1
804     */
805    public ConnectionConsumer createDurableConnectionConsumer(Topic topic, String subscriptionName, String messageSelector, ServerSessionPool sessionPool, int maxMessages,
806                                                              boolean noLocal) throws JMSException {
807        checkClosedOrFailed();
808
809        if (queueOnlyConnection) {
810            throw new IllegalStateException("QueueConnection cannot be used to create Pub/Sub based resources.");
811        }
812
813        ensureConnectionInfoSent();
814        SessionId sessionId = new SessionId(info.getConnectionId(), -1);
815        ConsumerInfo info = new ConsumerInfo(new ConsumerId(sessionId, consumerIdGenerator.getNextSequenceId()));
816        info.setDestination(ActiveMQMessageTransformation.transformDestination(topic));
817        info.setSubscriptionName(subscriptionName);
818        info.setSelector(messageSelector);
819        info.setPrefetchSize(maxMessages);
820        info.setDispatchAsync(isDispatchAsync());
821
822        // Allows the options on the destination to configure the consumerInfo
823        if (info.getDestination().getOptions() != null) {
824            Map<String, String> options = new HashMap<>(info.getDestination().getOptions());
825            IntrospectionSupport.setProperties(this.info, options, "consumer.");
826        }
827
828        return new ActiveMQConnectionConsumer(this, sessionPool, info);
829    }
830
831    // Properties
832    // -------------------------------------------------------------------------
833
834    /**
835     * Returns true if this connection has been started
836     *
837     * @return true if this Connection is started
838     */
839    public boolean isStarted() {
840        return started.get();
841    }
842
843    /**
844     * Returns true if the connection is closed
845     */
846    public boolean isClosed() {
847        return closed.get();
848    }
849
850    /**
851     * Returns true if the connection is in the process of being closed
852     */
853    public boolean isClosing() {
854        return closing.get();
855    }
856
857    /**
858     * Returns true if the underlying transport has failed
859     */
860    public boolean isTransportFailed() {
861        return transportFailed.get();
862    }
863
864    /**
865     * @return Returns the prefetchPolicy.
866     */
867    public ActiveMQPrefetchPolicy getPrefetchPolicy() {
868        return prefetchPolicy;
869    }
870
871    /**
872     * Sets the <a
873     * href="http://activemq.apache.org/what-is-the-prefetch-limit-for.html">prefetch
874     * policy</a> for consumers created by this connection.
875     */
876    public void setPrefetchPolicy(ActiveMQPrefetchPolicy prefetchPolicy) {
877        this.prefetchPolicy = prefetchPolicy;
878    }
879
880    /**
881     */
882    public Transport getTransportChannel() {
883        return transport;
884    }
885
886    /**
887     * @return Returns the clientID of the connection, forcing one to be
888     *         generated if one has not yet been configured.
889     */
890    public String getInitializedClientID() throws JMSException {
891        ensureConnectionInfoSent();
892        return info.getClientId();
893    }
894
895    /**
896     * @return Returns the timeStampsDisableByDefault.
897     */
898    public boolean isDisableTimeStampsByDefault() {
899        return disableTimeStampsByDefault;
900    }
901
902    /**
903     * Sets whether or not timestamps on messages should be disabled or not. If
904     * you disable them it adds a small performance boost.
905     */
906    public void setDisableTimeStampsByDefault(boolean timeStampsDisableByDefault) {
907        this.disableTimeStampsByDefault = timeStampsDisableByDefault;
908    }
909
910    /**
911     * @return Returns the dispatchOptimizedMessage.
912     */
913    public boolean isOptimizedMessageDispatch() {
914        return optimizedMessageDispatch;
915    }
916
917    /**
918     * If this flag is set then an larger prefetch limit is used - only
919     * applicable for durable topic subscribers.
920     */
921    public void setOptimizedMessageDispatch(boolean dispatchOptimizedMessage) {
922        this.optimizedMessageDispatch = dispatchOptimizedMessage;
923    }
924
925    /**
926     * @return Returns the closeTimeout.
927     */
928    public int getCloseTimeout() {
929        return closeTimeout;
930    }
931
932    /**
933     * Sets the timeout before a close is considered complete. Normally a
934     * close() on a connection waits for confirmation from the broker; this
935     * allows that operation to timeout to save the client hanging if there is
936     * no broker
937     */
938    public void setCloseTimeout(int closeTimeout) {
939        this.closeTimeout = closeTimeout;
940    }
941
942    /**
943     * @return ConnectionInfo
944     */
945    public ConnectionInfo getConnectionInfo() {
946        return this.info;
947    }
948
949    public boolean isUseRetroactiveConsumer() {
950        return useRetroactiveConsumer;
951    }
952
953    /**
954     * Sets whether or not retroactive consumers are enabled. Retroactive
955     * consumers allow non-durable topic subscribers to receive old messages
956     * that were published before the non-durable subscriber started.
957     */
958    public void setUseRetroactiveConsumer(boolean useRetroactiveConsumer) {
959        this.useRetroactiveConsumer = useRetroactiveConsumer;
960    }
961
962    public boolean isNestedMapAndListEnabled() {
963        return nestedMapAndListEnabled;
964    }
965
966    /**
967     * Enables/disables whether or not Message properties and MapMessage entries
968     * support <a
969     * href="http://activemq.apache.org/structured-message-properties-and-mapmessages.html">Nested
970     * Structures</a> of Map and List objects
971     */
972    public void setNestedMapAndListEnabled(boolean structuredMapsEnabled) {
973        this.nestedMapAndListEnabled = structuredMapsEnabled;
974    }
975
976    public boolean isExclusiveConsumer() {
977        return exclusiveConsumer;
978    }
979
980    /**
981     * Enables or disables whether or not queue consumers should be exclusive or
982     * not for example to preserve ordering when not using <a
983     * href="http://activemq.apache.org/message-groups.html">Message Groups</a>
984     *
985     * @param exclusiveConsumer
986     */
987    public void setExclusiveConsumer(boolean exclusiveConsumer) {
988        this.exclusiveConsumer = exclusiveConsumer;
989    }
990
991    /**
992     * Adds a transport listener so that a client can be notified of events in
993     * the underlying transport
994     */
995    public void addTransportListener(TransportListener transportListener) {
996        transportListeners.add(transportListener);
997    }
998
999    public void removeTransportListener(TransportListener transportListener) {
1000        transportListeners.remove(transportListener);
1001    }
1002
1003    public boolean isUseDedicatedTaskRunner() {
1004        return useDedicatedTaskRunner;
1005    }
1006
1007    public void setUseDedicatedTaskRunner(boolean useDedicatedTaskRunner) {
1008        this.useDedicatedTaskRunner = useDedicatedTaskRunner;
1009    }
1010
1011    public TaskRunnerFactory getSessionTaskRunner() {
1012        synchronized (this) {
1013            if (sessionTaskRunner == null) {
1014                sessionTaskRunner = new TaskRunnerFactory("ActiveMQ Session Task", ThreadPriorities.INBOUND_CLIENT_SESSION, false, 1000, isUseDedicatedTaskRunner(), maxThreadPoolSize);
1015                sessionTaskRunner.setRejectedTaskHandler(rejectedTaskHandler);
1016            }
1017        }
1018        return sessionTaskRunner;
1019    }
1020
1021    public void setSessionTaskRunner(TaskRunnerFactory sessionTaskRunner) {
1022        this.sessionTaskRunner = sessionTaskRunner;
1023    }
1024
1025    public MessageTransformer getTransformer() {
1026        return transformer;
1027    }
1028
1029    /**
1030     * Sets the transformer used to transform messages before they are sent on
1031     * to the JMS bus or when they are received from the bus but before they are
1032     * delivered to the JMS client
1033     */
1034    public void setTransformer(MessageTransformer transformer) {
1035        this.transformer = transformer;
1036    }
1037
1038    /**
1039     * @return the statsEnabled
1040     */
1041    public boolean isStatsEnabled() {
1042        return this.stats.isEnabled();
1043    }
1044
1045    /**
1046     * @param statsEnabled the statsEnabled to set
1047     */
1048    public void setStatsEnabled(boolean statsEnabled) {
1049        this.stats.setEnabled(statsEnabled);
1050    }
1051
1052    /**
1053     * Returns the {@link DestinationSource} object which can be used to listen to destinations
1054     * being created or destroyed or to enquire about the current destinations available on the broker
1055     *
1056     * @return a lazily created destination source
1057     * @throws JMSException
1058     */
1059    @Override
1060    public DestinationSource getDestinationSource() throws JMSException {
1061        if (destinationSource == null) {
1062            destinationSource = new DestinationSource(this);
1063            destinationSource.start();
1064        }
1065        return destinationSource;
1066    }
1067
1068    // Implementation methods
1069    // -------------------------------------------------------------------------
1070
1071    /**
1072     * Used internally for adding Sessions to the Connection
1073     *
1074     * @param session
1075     * @throws JMSException
1076     * @throws JMSException
1077     */
1078    protected void addSession(ActiveMQSession session) throws JMSException {
1079        this.sessions.add(session);
1080        if (sessions.size() > 1 || session.isTransacted()) {
1081            optimizedMessageDispatch = false;
1082        }
1083    }
1084
1085    /**
1086     * Used interanlly for removing Sessions from a Connection
1087     *
1088     * @param session
1089     */
1090    protected void removeSession(ActiveMQSession session) {
1091        this.sessions.remove(session);
1092        this.removeDispatcher(session);
1093    }
1094
1095    /**
1096     * Add a ConnectionConsumer
1097     *
1098     * @param connectionConsumer
1099     * @throws JMSException
1100     */
1101    protected void addConnectionConsumer(ActiveMQConnectionConsumer connectionConsumer) throws JMSException {
1102        this.connectionConsumers.add(connectionConsumer);
1103    }
1104
1105    /**
1106     * Remove a ConnectionConsumer
1107     *
1108     * @param connectionConsumer
1109     */
1110    protected void removeConnectionConsumer(ActiveMQConnectionConsumer connectionConsumer) {
1111        this.connectionConsumers.remove(connectionConsumer);
1112        this.removeDispatcher(connectionConsumer);
1113    }
1114
1115    /**
1116     * Creates a <CODE>TopicSession</CODE> object.
1117     *
1118     * @param transacted indicates whether the session is transacted
1119     * @param acknowledgeMode indicates whether the consumer or the client will
1120     *                acknowledge any messages it receives; ignored if the
1121     *                session is transacted. Legal values are
1122     *                <code>Session.AUTO_ACKNOWLEDGE</code>,
1123     *                <code>Session.CLIENT_ACKNOWLEDGE</code>, and
1124     *                <code>Session.DUPS_OK_ACKNOWLEDGE</code>.
1125     * @return a newly created topic session
1126     * @throws JMSException if the <CODE>TopicConnection</CODE> object fails
1127     *                 to create a session due to some internal error or lack of
1128     *                 support for the specific transaction and acknowledgement
1129     *                 mode.
1130     * @see Session#AUTO_ACKNOWLEDGE
1131     * @see Session#CLIENT_ACKNOWLEDGE
1132     * @see Session#DUPS_OK_ACKNOWLEDGE
1133     */
1134    @Override
1135    public TopicSession createTopicSession(boolean transacted, int acknowledgeMode) throws JMSException {
1136        return new ActiveMQTopicSession((ActiveMQSession)createSession(transacted, acknowledgeMode));
1137    }
1138
1139    /**
1140     * Creates a connection consumer for this connection (optional operation).
1141     * This is an expert facility not used by regular JMS clients.
1142     *
1143     * @param topic the topic to access
1144     * @param messageSelector only messages with properties matching the message
1145     *                selector expression are delivered. A value of null or an
1146     *                empty string indicates that there is no message selector
1147     *                for the message consumer.
1148     * @param sessionPool the server session pool to associate with this
1149     *                connection consumer
1150     * @param maxMessages the maximum number of messages that can be assigned to
1151     *                a server session at one time
1152     * @return the connection consumer
1153     * @throws JMSException if the <CODE>TopicConnection</CODE> object fails
1154     *                 to create a connection consumer due to some internal
1155     *                 error or invalid arguments for <CODE>sessionPool</CODE>
1156     *                 and <CODE>messageSelector</CODE>.
1157     * @throws javax.jms.InvalidDestinationException if an invalid topic is
1158     *                 specified.
1159     * @throws javax.jms.InvalidSelectorException if the message selector is
1160     *                 invalid.
1161     * @see javax.jms.ConnectionConsumer
1162     */
1163    @Override
1164    public ConnectionConsumer createConnectionConsumer(Topic topic, String messageSelector, ServerSessionPool sessionPool, int maxMessages) throws JMSException {
1165        return createConnectionConsumer(topic, messageSelector, sessionPool, maxMessages, false);
1166    }
1167
1168    /**
1169     * Creates a connection consumer for this connection (optional operation).
1170     * This is an expert facility not used by regular JMS clients.
1171     *
1172     * @param queue the queue to access
1173     * @param messageSelector only messages with properties matching the message
1174     *                selector expression are delivered. A value of null or an
1175     *                empty string indicates that there is no message selector
1176     *                for the message consumer.
1177     * @param sessionPool the server session pool to associate with this
1178     *                connection consumer
1179     * @param maxMessages the maximum number of messages that can be assigned to
1180     *                a server session at one time
1181     * @return the connection consumer
1182     * @throws JMSException if the <CODE>QueueConnection</CODE> object fails
1183     *                 to create a connection consumer due to some internal
1184     *                 error or invalid arguments for <CODE>sessionPool</CODE>
1185     *                 and <CODE>messageSelector</CODE>.
1186     * @throws javax.jms.InvalidDestinationException if an invalid queue is
1187     *                 specified.
1188     * @throws javax.jms.InvalidSelectorException if the message selector is
1189     *                 invalid.
1190     * @see javax.jms.ConnectionConsumer
1191     */
1192    @Override
1193    public ConnectionConsumer createConnectionConsumer(Queue queue, String messageSelector, ServerSessionPool sessionPool, int maxMessages) throws JMSException {
1194        return createConnectionConsumer(queue, messageSelector, sessionPool, maxMessages, false);
1195    }
1196
1197    /**
1198     * Creates a connection consumer for this connection (optional operation).
1199     * This is an expert facility not used by regular JMS clients.
1200     *
1201     * @param destination the destination to access
1202     * @param messageSelector only messages with properties matching the message
1203     *                selector expression are delivered. A value of null or an
1204     *                empty string indicates that there is no message selector
1205     *                for the message consumer.
1206     * @param sessionPool the server session pool to associate with this
1207     *                connection consumer
1208     * @param maxMessages the maximum number of messages that can be assigned to
1209     *                a server session at one time
1210     * @return the connection consumer
1211     * @throws JMSException if the <CODE>Connection</CODE> object fails to
1212     *                 create a connection consumer due to some internal error
1213     *                 or invalid arguments for <CODE>sessionPool</CODE> and
1214     *                 <CODE>messageSelector</CODE>.
1215     * @throws javax.jms.InvalidDestinationException if an invalid destination
1216     *                 is specified.
1217     * @throws javax.jms.InvalidSelectorException if the message selector is
1218     *                 invalid.
1219     * @see javax.jms.ConnectionConsumer
1220     * @since 1.1
1221     */
1222    @Override
1223    public ConnectionConsumer createConnectionConsumer(Destination destination, String messageSelector, ServerSessionPool sessionPool, int maxMessages) throws JMSException {
1224        return createConnectionConsumer(destination, messageSelector, sessionPool, maxMessages, false);
1225    }
1226
1227    public ConnectionConsumer createConnectionConsumer(Destination destination, String messageSelector, ServerSessionPool sessionPool, int maxMessages, boolean noLocal)
1228        throws JMSException {
1229
1230        checkClosedOrFailed();
1231        ensureConnectionInfoSent();
1232
1233        ConsumerId consumerId = createConsumerId();
1234        ConsumerInfo consumerInfo = new ConsumerInfo(consumerId);
1235        consumerInfo.setDestination(ActiveMQMessageTransformation.transformDestination(destination));
1236        consumerInfo.setSelector(messageSelector);
1237        consumerInfo.setPrefetchSize(maxMessages);
1238        consumerInfo.setNoLocal(noLocal);
1239        consumerInfo.setDispatchAsync(isDispatchAsync());
1240
1241        // Allows the options on the destination to configure the consumerInfo
1242        if (consumerInfo.getDestination().getOptions() != null) {
1243            Map<String, String> options = new HashMap<>(consumerInfo.getDestination().getOptions());
1244            IntrospectionSupport.setProperties(consumerInfo, options, "consumer.");
1245        }
1246
1247        return new ActiveMQConnectionConsumer(this, sessionPool, consumerInfo);
1248    }
1249
1250    /**
1251     * @return a newly created ConsumedId unique to this connection session instance.
1252     */
1253    private ConsumerId createConsumerId() {
1254        return new ConsumerId(connectionSessionId, consumerIdGenerator.getNextSequenceId());
1255    }
1256
1257    /**
1258     * Creates a <CODE>QueueSession</CODE> object.
1259     *
1260     * @param transacted indicates whether the session is transacted
1261     * @param acknowledgeMode indicates whether the consumer or the client will
1262     *                acknowledge any messages it receives; ignored if the
1263     *                session is transacted. Legal values are
1264     *                <code>Session.AUTO_ACKNOWLEDGE</code>,
1265     *                <code>Session.CLIENT_ACKNOWLEDGE</code>, and
1266     *                <code>Session.DUPS_OK_ACKNOWLEDGE</code>.
1267     * @return a newly created queue session
1268     * @throws JMSException if the <CODE>QueueConnection</CODE> object fails
1269     *                 to create a session due to some internal error or lack of
1270     *                 support for the specific transaction and acknowledgement
1271     *                 mode.
1272     * @see Session#AUTO_ACKNOWLEDGE
1273     * @see Session#CLIENT_ACKNOWLEDGE
1274     * @see Session#DUPS_OK_ACKNOWLEDGE
1275     */
1276    @Override
1277    public QueueSession createQueueSession(boolean transacted, int acknowledgeMode) throws JMSException {
1278        return new ActiveMQQueueSession((ActiveMQSession)createSession(transacted, acknowledgeMode));
1279    }
1280
1281    /**
1282     * Ensures that the clientID was manually specified and not auto-generated.
1283     * If the clientID was not specified this method will throw an exception.
1284     * This method is used to ensure that the clientID + durableSubscriber name
1285     * are used correctly.
1286     *
1287     * @throws JMSException
1288     */
1289    public void checkClientIDWasManuallySpecified() throws JMSException {
1290        if (!userSpecifiedClientID) {
1291            throw new JMSException("You cannot create a durable subscriber without specifying a unique clientID on a Connection");
1292        }
1293    }
1294
1295    /**
1296     * send a Packet through the Connection - for internal use only
1297     *
1298     * @param command
1299     * @throws JMSException
1300     */
1301    public void asyncSendPacket(Command command) throws JMSException {
1302        if (isClosed()) {
1303            throw new ConnectionClosedException();
1304        } else {
1305            doAsyncSendPacket(command);
1306        }
1307    }
1308
1309    private void doAsyncSendPacket(Command command) throws JMSException {
1310        try {
1311            this.transport.oneway(command);
1312        } catch (IOException e) {
1313            throw JMSExceptionSupport.create(e);
1314        }
1315    }
1316
1317    /**
1318     * Send a packet through a Connection - for internal use only
1319     *
1320     * @param command
1321     *
1322     * @throws JMSException
1323     */
1324    public void syncSendPacket(final Command command, final AsyncCallback onComplete) throws JMSException {
1325        if(onComplete==null) {
1326            syncSendPacket(command);
1327        } else {
1328            if (isClosed()) {
1329                throw new ConnectionClosedException();
1330            }
1331            try {
1332                this.transport.asyncRequest(command, new ResponseCallback() {
1333                    @Override
1334                    public void onCompletion(FutureResponse resp) {
1335                        Response response;
1336                        Throwable exception = null;
1337                        try {
1338                            response = resp.getResult();
1339                            if (response.isException()) {
1340                                ExceptionResponse er = (ExceptionResponse)response;
1341                                exception = er.getException();
1342                            }
1343                        } catch (Exception e) {
1344                            exception = e;
1345                        }
1346                        if (exception != null) {
1347                            if ( exception instanceof JMSException) {
1348                                onComplete.onException((JMSException) exception);
1349                            } else {
1350                                if (isClosed() || closing.get()) {
1351                                    LOG.debug("Received an exception but connection is closing");
1352                                }
1353                                JMSException jmsEx = null;
1354                                try {
1355                                    jmsEx = JMSExceptionSupport.create(exception);
1356                                } catch(Throwable e) {
1357                                    LOG.error("Caught an exception trying to create a JMSException for " +exception,e);
1358                                }
1359                                // dispose of transport for security exceptions on connection initiation
1360                                if (exception instanceof SecurityException && command instanceof ConnectionInfo){
1361                                    try {
1362                                        forceCloseOnSecurityException(exception);
1363                                    } catch (Throwable t) {
1364                                        // We throw the original error from the ExceptionResponse instead.
1365                                    }
1366                                }
1367                                if (jmsEx != null) {
1368                                    onComplete.onException(jmsEx);
1369                                }
1370                            }
1371                        } else {
1372                            onComplete.onSuccess();
1373                        }
1374                    }
1375                });
1376            } catch (IOException e) {
1377                throw JMSExceptionSupport.create(e);
1378            }
1379        }
1380    }
1381
1382    private void forceCloseOnSecurityException(Throwable exception) {
1383        LOG.trace("force close on security exception:{}, transport={}", this, transport, exception);
1384        onException(new IOException("Force close due to SecurityException on connect", exception));
1385    }
1386
1387    public Response syncSendPacket(Command command, int timeout) throws JMSException {
1388        if (isClosed()) {
1389            throw new ConnectionClosedException();
1390        } else {
1391            try {
1392                Response response = (Response)(timeout > 0
1393                        ? this.transport.request(command, timeout)
1394                        : this.transport.request(command));
1395                if (response.isException()) {
1396                    ExceptionResponse er = (ExceptionResponse)response;
1397                    if (er.getException() instanceof JMSException) {
1398                        throw (JMSException)er.getException();
1399                    } else {
1400                        if (isClosed() || closing.get()) {
1401                            LOG.debug("Received an exception but connection is closing");
1402                        }
1403                        JMSException jmsEx = null;
1404                        try {
1405                            jmsEx = JMSExceptionSupport.create(er.getException());
1406                        } catch(Throwable e) {
1407                            LOG.error("Caught an exception trying to create a JMSException for " +er.getException(),e);
1408                        }
1409                        if (er.getException() instanceof SecurityException && command instanceof ConnectionInfo){
1410                            try {
1411                                forceCloseOnSecurityException(er.getException());
1412                            } catch (Throwable t) {
1413                                // We throw the original error from the ExceptionResponse instead.
1414                            }
1415                        }
1416                        if (jmsEx != null) {
1417                            throw jmsEx;
1418                        }
1419                    }
1420                }
1421                return response;
1422            } catch (IOException e) {
1423                throw JMSExceptionSupport.create(e);
1424            }
1425        }
1426    }
1427
1428    /**
1429     * Send a packet through a Connection - for internal use only
1430     *
1431     * @param command
1432     *
1433     * @return the broker Response for the given Command.
1434     *
1435     * @throws JMSException
1436     */
1437    public Response syncSendPacket(Command command) throws JMSException {
1438        return syncSendPacket(command, 0);
1439    }
1440
1441    /**
1442     * @return statistics for this Connection
1443     */
1444    @Override
1445    public StatsImpl getStats() {
1446        return stats;
1447    }
1448
1449    /**
1450     * simply throws an exception if the Connection is already closed or the
1451     * Transport has failed
1452     *
1453     * @throws JMSException
1454     */
1455    protected synchronized void checkClosedOrFailed() throws JMSException {
1456        checkClosed();
1457        if (transportFailed.get()) {
1458            throw new ConnectionFailedException(firstFailureError);
1459        }
1460    }
1461
1462    /**
1463     * simply throws an exception if the Connection is already closed
1464     *
1465     * @throws JMSException
1466     */
1467    protected synchronized void checkClosed() throws JMSException {
1468        if (closed.get()) {
1469            throw new ConnectionClosedException();
1470        }
1471    }
1472
1473    /**
1474     * Send the ConnectionInfo to the Broker
1475     *
1476     * @throws JMSException
1477     */
1478    protected void ensureConnectionInfoSent() throws JMSException {
1479        synchronized(this.ensureConnectionInfoSentMutex) {
1480            // Can we skip sending the ConnectionInfo packet??
1481            if (isConnectionInfoSentToBroker || closed.get()) {
1482                return;
1483            }
1484            //TODO shouldn't this check be on userSpecifiedClientID rather than the value of clientID?
1485            if (info.getClientId() == null || info.getClientId().trim().length() == 0) {
1486                info.setClientId(clientIdGenerator.generateId());
1487            }
1488            syncSendPacket(info.copy(), getConnectResponseTimeout());
1489
1490            this.isConnectionInfoSentToBroker = true;
1491            // Add a temp destination advisory consumer so that
1492            // We know what the valid temporary destinations are on the
1493            // broker without having to do an RPC to the broker.
1494
1495            ConsumerId consumerId = new ConsumerId(new SessionId(info.getConnectionId(), -1), consumerIdGenerator.getNextSequenceId());
1496            if (watchTopicAdvisories) {
1497                advisoryConsumer = new AdvisoryConsumer(this, consumerId);
1498            }
1499        }
1500    }
1501
1502    public synchronized boolean isWatchTopicAdvisories() {
1503        return watchTopicAdvisories;
1504    }
1505
1506    public synchronized void setWatchTopicAdvisories(boolean watchTopicAdvisories) {
1507        this.watchTopicAdvisories = watchTopicAdvisories;
1508    }
1509
1510    /**
1511     * @return Returns the useAsyncSend.
1512     */
1513    public boolean isUseAsyncSend() {
1514        return useAsyncSend;
1515    }
1516
1517    /**
1518     * Forces the use of <a
1519     * href="http://activemq.apache.org/async-sends.html">Async Sends</a> which
1520     * adds a massive performance boost; but means that the send() method will
1521     * return immediately whether the message has been sent or not which could
1522     * lead to message loss.
1523     */
1524    public void setUseAsyncSend(boolean useAsyncSend) {
1525        this.useAsyncSend = useAsyncSend;
1526    }
1527
1528    /**
1529     * @return true if always sync send messages
1530     */
1531    public boolean isAlwaysSyncSend() {
1532        return this.alwaysSyncSend;
1533    }
1534
1535    /**
1536     * Set true if always require messages to be sync sent
1537     *
1538     * @param alwaysSyncSend
1539     */
1540    public void setAlwaysSyncSend(boolean alwaysSyncSend) {
1541        this.alwaysSyncSend = alwaysSyncSend;
1542    }
1543
1544    /**
1545     * @return the messagePrioritySupported
1546     */
1547    public boolean isMessagePrioritySupported() {
1548        return this.messagePrioritySupported;
1549    }
1550
1551    /**
1552     * @param messagePrioritySupported the messagePrioritySupported to set
1553     */
1554    public void setMessagePrioritySupported(boolean messagePrioritySupported) {
1555        this.messagePrioritySupported = messagePrioritySupported;
1556    }
1557
1558    /**
1559     * Cleans up this connection so that it's state is as if the connection was
1560     * just created. This allows the Resource Adapter to clean up a connection
1561     * so that it can be reused without having to close and recreate the
1562     * connection.
1563     */
1564    public void cleanup() throws JMSException {
1565        doCleanup(false);
1566    }
1567
1568    public boolean isUserSpecifiedClientID() {
1569        return userSpecifiedClientID;
1570    }
1571
1572    public void doCleanup(boolean removeConnection) throws JMSException {
1573        if (advisoryConsumer != null && !isTransportFailed()) {
1574            advisoryConsumer.dispose();
1575            advisoryConsumer = null;
1576        }
1577
1578        for (Iterator<ActiveMQSession> i = this.sessions.iterator(); i.hasNext();) {
1579            ActiveMQSession s = i.next();
1580            s.dispose();
1581        }
1582        for (Iterator<ActiveMQConnectionConsumer> i = this.connectionConsumers.iterator(); i.hasNext();) {
1583            ActiveMQConnectionConsumer c = i.next();
1584            c.dispose();
1585        }
1586
1587        if (removeConnection) {
1588            if (isConnectionInfoSentToBroker) {
1589                if (!transportFailed.get() && !closing.get()) {
1590                    syncSendPacket(info.createRemoveCommand());
1591                }
1592                isConnectionInfoSentToBroker = false;
1593            }
1594            if (userSpecifiedClientID) {
1595                info.setClientId(null);
1596                userSpecifiedClientID = false;
1597            }
1598            clientIDSet = false;
1599        }
1600
1601        started.set(false);
1602    }
1603
1604    /**
1605     * Changes the associated username/password that is associated with this
1606     * connection. If the connection has been used, you must called cleanup()
1607     * before calling this method.
1608     *
1609     * @throws IllegalStateException if the connection is in used.
1610     */
1611    public void changeUserInfo(String userName, String password) throws JMSException {
1612        if (isConnectionInfoSentToBroker) {
1613            throw new IllegalStateException("changeUserInfo used Connection is not allowed");
1614        }
1615        this.info.setUserName(userName);
1616        this.info.setPassword(password);
1617    }
1618
1619    /**
1620     * @return Returns the resourceManagerId.
1621     * @throws JMSException
1622     */
1623    public String getResourceManagerId() throws JMSException {
1624        if (isRmIdFromConnectionId()) {
1625            return info.getConnectionId().getValue();
1626        }
1627        waitForBrokerInfo();
1628        if (brokerInfo == null) {
1629            throw new JMSException("Connection failed before Broker info was received.");
1630        }
1631        return brokerInfo.getBrokerId().getValue();
1632    }
1633
1634    /**
1635     * Returns the broker name if one is available or null if one is not
1636     * available yet.
1637     */
1638    public String getBrokerName() {
1639        try {
1640            brokerInfoReceived.await(5, TimeUnit.SECONDS);
1641            if (brokerInfo == null) {
1642                return null;
1643            }
1644            return brokerInfo.getBrokerName();
1645        } catch (InterruptedException e) {
1646            Thread.currentThread().interrupt();
1647            return null;
1648        }
1649    }
1650
1651    /**
1652     * Returns the broker information if it is available or null if it is not
1653     * available yet.
1654     */
1655    public BrokerInfo getBrokerInfo() {
1656        return brokerInfo;
1657    }
1658
1659    /**
1660     * @return Returns the RedeliveryPolicy.
1661     * @throws JMSException
1662     */
1663    public RedeliveryPolicy getRedeliveryPolicy() throws JMSException {
1664        return redeliveryPolicyMap.getDefaultEntry();
1665    }
1666
1667    /**
1668     * Sets the redelivery policy to be used when messages are rolled back
1669     */
1670    public void setRedeliveryPolicy(RedeliveryPolicy redeliveryPolicy) {
1671        this.redeliveryPolicyMap.setDefaultEntry(redeliveryPolicy);
1672    }
1673
1674    public BlobTransferPolicy getBlobTransferPolicy() {
1675        if (blobTransferPolicy == null) {
1676            blobTransferPolicy = createBlobTransferPolicy();
1677        }
1678        return blobTransferPolicy;
1679    }
1680
1681    /**
1682     * Sets the policy used to describe how out-of-band BLOBs (Binary Large
1683     * OBjects) are transferred from producers to brokers to consumers
1684     */
1685    public void setBlobTransferPolicy(BlobTransferPolicy blobTransferPolicy) {
1686        this.blobTransferPolicy = blobTransferPolicy;
1687    }
1688
1689    /**
1690     * @return Returns the alwaysSessionAsync.
1691     */
1692    public boolean isAlwaysSessionAsync() {
1693        return alwaysSessionAsync;
1694    }
1695
1696    /**
1697     * If this flag is not set then a separate thread is not used for dispatching messages for each Session in
1698     * the Connection. However, a separate thread is always used if there is more than one session, or the session
1699     * isn't in auto acknowledge or duplicates ok mode.  By default this value is set to true and session dispatch
1700     * happens asynchronously.
1701     */
1702    public void setAlwaysSessionAsync(boolean alwaysSessionAsync) {
1703        this.alwaysSessionAsync = alwaysSessionAsync;
1704    }
1705
1706    /**
1707     * @return Returns the optimizeAcknowledge.
1708     */
1709    public boolean isOptimizeAcknowledge() {
1710        return optimizeAcknowledge;
1711    }
1712
1713    /**
1714     * Enables an optimised acknowledgement mode where messages are acknowledged
1715     * in batches rather than individually
1716     *
1717     * @param optimizeAcknowledge The optimizeAcknowledge to set.
1718     */
1719    public void setOptimizeAcknowledge(boolean optimizeAcknowledge) {
1720        this.optimizeAcknowledge = optimizeAcknowledge;
1721    }
1722
1723    /**
1724     * The max time in milliseconds between optimized ack batches
1725     * @param optimizeAcknowledgeTimeOut
1726     */
1727    public void setOptimizeAcknowledgeTimeOut(long optimizeAcknowledgeTimeOut) {
1728        this.optimizeAcknowledgeTimeOut =  optimizeAcknowledgeTimeOut;
1729    }
1730
1731    public long getOptimizeAcknowledgeTimeOut() {
1732        return optimizeAcknowledgeTimeOut;
1733    }
1734
1735    public long getWarnAboutUnstartedConnectionTimeout() {
1736        return warnAboutUnstartedConnectionTimeout;
1737    }
1738
1739    /**
1740     * Enables the timeout from a connection creation to when a warning is
1741     * generated if the connection is not properly started via {@link #start()}
1742     * and a message is received by a consumer. It is a very common gotcha to
1743     * forget to <a
1744     * href="http://activemq.apache.org/i-am-not-receiving-any-messages-what-is-wrong.html">start
1745     * the connection</a> so this option makes the default case to create a
1746     * warning if the user forgets. To disable the warning just set the value to <
1747     * 0 (say -1).
1748     */
1749    public void setWarnAboutUnstartedConnectionTimeout(long warnAboutUnstartedConnectionTimeout) {
1750        this.warnAboutUnstartedConnectionTimeout = warnAboutUnstartedConnectionTimeout;
1751    }
1752
1753    /**
1754     * @return the sendTimeout (in milliseconds)
1755     */
1756    public int getSendTimeout() {
1757        return sendTimeout;
1758    }
1759
1760    /**
1761     * @param sendTimeout the sendTimeout to set (in milliseconds)
1762     */
1763    public void setSendTimeout(int sendTimeout) {
1764        this.sendTimeout = sendTimeout;
1765    }
1766
1767    /**
1768     * @return the sendAcksAsync
1769     */
1770    public boolean isSendAcksAsync() {
1771        return sendAcksAsync;
1772    }
1773
1774    /**
1775     * @param sendAcksAsync the sendAcksAsync to set
1776     */
1777    public void setSendAcksAsync(boolean sendAcksAsync) {
1778        this.sendAcksAsync = sendAcksAsync;
1779    }
1780
1781    /**
1782     * Returns the time this connection was created
1783     */
1784    public long getTimeCreated() {
1785        return timeCreated;
1786    }
1787
1788    private void waitForBrokerInfo() throws JMSException {
1789        try {
1790            brokerInfoReceived.await();
1791        } catch (InterruptedException e) {
1792            Thread.currentThread().interrupt();
1793            throw JMSExceptionSupport.create(e);
1794        }
1795    }
1796
1797    // Package protected so that it can be used in unit tests
1798    public Transport getTransport() {
1799        return transport;
1800    }
1801
1802    public void addProducer(ProducerId producerId, ActiveMQMessageProducer producer) {
1803        producers.put(producerId, producer);
1804    }
1805
1806    public void removeProducer(ProducerId producerId) {
1807        producers.remove(producerId);
1808    }
1809
1810    public void addDispatcher(ConsumerId consumerId, ActiveMQDispatcher dispatcher) {
1811        dispatchers.put(consumerId, dispatcher);
1812    }
1813
1814    public void removeDispatcher(ConsumerId consumerId) {
1815        dispatchers.remove(consumerId);
1816    }
1817
1818    public boolean hasDispatcher(ConsumerId consumerId) {
1819        return dispatchers.containsKey(consumerId);
1820    }
1821
1822    /**
1823     * @param o - the command to consume
1824     */
1825    @Override
1826    public void onCommand(final Object o) {
1827        final Command command = (Command)o;
1828        if (!closed.get() && command != null) {
1829            try {
1830                command.visit(new CommandVisitorAdapter() {
1831                    @Override
1832                    public Response processMessageDispatch(MessageDispatch md) throws Exception {
1833                        waitForTransportInterruptionProcessingToComplete();
1834                        ActiveMQDispatcher dispatcher = dispatchers.get(md.getConsumerId());
1835                        if (dispatcher != null) {
1836                            // Copy in case a embedded broker is dispatching via
1837                            // vm://
1838                            // md.getMessage() == null to signal end of queue
1839                            // browse.
1840                            Message msg = md.getMessage();
1841                            if (msg != null) {
1842                                msg = msg.copy();
1843                                msg.setReadOnlyBody(true);
1844                                msg.setReadOnlyProperties(true);
1845                                msg.setRedeliveryCounter(md.getRedeliveryCounter());
1846                                msg.setConnection(ActiveMQConnection.this);
1847                                msg.setMemoryUsage(null);
1848                                md.setMessage(msg);
1849                            }
1850                            dispatcher.dispatch(md);
1851                        } else {
1852                            LOG.debug("{} no dispatcher for {} in {}", this, md, dispatchers);
1853                        }
1854                        return null;
1855                    }
1856
1857                    @Override
1858                    public Response processProducerAck(ProducerAck pa) throws Exception {
1859                        if (pa != null && pa.getProducerId() != null) {
1860                            ActiveMQMessageProducer producer = producers.get(pa.getProducerId());
1861                            if (producer != null) {
1862                                producer.onProducerAck(pa);
1863                            }
1864                        }
1865                        return null;
1866                    }
1867
1868                    @Override
1869                    public Response processBrokerInfo(BrokerInfo info) throws Exception {
1870                        brokerInfo = info;
1871                        brokerInfoReceived.countDown();
1872                        optimizeAcknowledge &= !brokerInfo.isFaultTolerantConfiguration();
1873                        getBlobTransferPolicy().setBrokerUploadUrl(info.getBrokerUploadUrl());
1874                        return null;
1875                    }
1876
1877                    @Override
1878                    public Response processConnectionError(final ConnectionError error) throws Exception {
1879                        executor.execute(new Runnable() {
1880                            @Override
1881                            public void run() {
1882                                onAsyncException(error.getException());
1883                            }
1884                        });
1885                        return null;
1886                    }
1887
1888                    @Override
1889                    public Response processControlCommand(ControlCommand command) throws Exception {
1890                        return null;
1891                    }
1892
1893                    @Override
1894                    public Response processConnectionControl(ConnectionControl control) throws Exception {
1895                        onConnectionControl((ConnectionControl)command);
1896                        return null;
1897                    }
1898
1899                    @Override
1900                    public Response processConsumerControl(ConsumerControl control) throws Exception {
1901                        onConsumerControl((ConsumerControl)command);
1902                        return null;
1903                    }
1904
1905                    @Override
1906                    public Response processWireFormat(WireFormatInfo info) throws Exception {
1907                        onWireFormatInfo((WireFormatInfo)command);
1908                        return null;
1909                    }
1910                });
1911            } catch (Exception e) {
1912                onClientInternalException(e);
1913            }
1914        }
1915
1916        for (Iterator<TransportListener> iter = transportListeners.iterator(); iter.hasNext();) {
1917            TransportListener listener = iter.next();
1918            listener.onCommand(command);
1919        }
1920    }
1921
1922    protected void onWireFormatInfo(WireFormatInfo info) {
1923        protocolVersion.set(info.getVersion());
1924
1925        long tmpMaxFrameSize = 0;
1926        try {
1927            tmpMaxFrameSize = info.getMaxFrameSize();
1928        } catch (IOException e) {
1929            // unmarshal error on property map
1930        }
1931
1932        if(tmpMaxFrameSize > 0) {
1933            maxFrameSize.set(tmpMaxFrameSize);
1934        }
1935    }
1936
1937    /**
1938     * Handles async client internal exceptions.
1939     * A client internal exception is usually one that has been thrown
1940     * by a container runtime component during asynchronous processing of a
1941     * message that does not affect the connection itself.
1942     * This method notifies the <code>ClientInternalExceptionListener</code> by invoking
1943     * its <code>onException</code> method, if one has been registered with this connection.
1944     *
1945     * @param error the exception that the problem
1946     */
1947    public void onClientInternalException(final Throwable error) {
1948        if ( !closed.get() && !closing.get() ) {
1949            if ( this.clientInternalExceptionListener != null ) {
1950                executor.execute(new Runnable() {
1951                    @Override
1952                    public void run() {
1953                        ActiveMQConnection.this.clientInternalExceptionListener.onException(error);
1954                    }
1955                });
1956            } else {
1957                LOG.debug("Async client internal exception occurred with no exception listener registered: {}",
1958                        error, error);
1959            }
1960        }
1961    }
1962
1963    /**
1964     * Used for handling async exceptions
1965     *
1966     * @param error
1967     */
1968    public void onAsyncException(Throwable error) {
1969        if (!closed.get() && !closing.get()) {
1970            if (this.exceptionListener != null) {
1971
1972                if (!(error instanceof JMSException)) {
1973                    error = JMSExceptionSupport.create(error);
1974                }
1975                final JMSException e = (JMSException)error;
1976
1977                executor.execute(new Runnable() {
1978                    @Override
1979                    public void run() {
1980                        ActiveMQConnection.this.exceptionListener.onException(e);
1981                    }
1982                });
1983
1984            } else {
1985                LOG.debug("Async exception with no exception listener: {}", error, error);
1986            }
1987        }
1988    }
1989
1990    @Override
1991    public void onException(final IOException error) {
1992        onAsyncException(error);
1993        if (!closed.get() && !closing.get()) {
1994            executor.execute(new Runnable() {
1995                @Override
1996                public void run() {
1997                    transportFailed(error);
1998                    ServiceSupport.dispose(ActiveMQConnection.this.transport);
1999                    brokerInfoReceived.countDown();
2000                    try {
2001                        doCleanup(true);
2002                    } catch (JMSException e) {
2003                        LOG.warn("Exception during connection cleanup, " + e, e);
2004                    }
2005                    for (Iterator<TransportListener> iter = transportListeners.iterator(); iter.hasNext();) {
2006                        TransportListener listener = iter.next();
2007                        listener.onException(error);
2008                    }
2009                }
2010            });
2011        }
2012    }
2013
2014    @Override
2015    public void transportInterupted() {
2016        transportInterruptionProcessingComplete.set(1);
2017        for (Iterator<ActiveMQSession> i = this.sessions.iterator(); i.hasNext();) {
2018            ActiveMQSession s = i.next();
2019            s.clearMessagesInProgress(transportInterruptionProcessingComplete);
2020        }
2021
2022        for (ActiveMQConnectionConsumer connectionConsumer : this.connectionConsumers) {
2023            connectionConsumer.clearMessagesInProgress(transportInterruptionProcessingComplete);
2024        }
2025
2026        if (transportInterruptionProcessingComplete.decrementAndGet() > 0) {
2027            if (LOG.isDebugEnabled()) {
2028                LOG.debug("transport interrupted - processing required, dispatchers: " + transportInterruptionProcessingComplete.get());
2029            }
2030            signalInterruptionProcessingNeeded();
2031        }
2032
2033        for (Iterator<TransportListener> iter = transportListeners.iterator(); iter.hasNext();) {
2034            TransportListener listener = iter.next();
2035            listener.transportInterupted();
2036        }
2037    }
2038
2039    @Override
2040    public void transportResumed() {
2041        for (Iterator<TransportListener> iter = transportListeners.iterator(); iter.hasNext();) {
2042            TransportListener listener = iter.next();
2043            listener.transportResumed();
2044        }
2045    }
2046
2047    /**
2048     * Create the DestinationInfo object for the temporary destination.
2049     *
2050     * @param topic - if its true topic, else queue.
2051     * @return DestinationInfo
2052     * @throws JMSException
2053     */
2054    protected ActiveMQTempDestination createTempDestination(boolean topic) throws JMSException {
2055
2056        // Check if Destination info is of temporary type.
2057        ActiveMQTempDestination dest;
2058        if (topic) {
2059            dest = new ActiveMQTempTopic(info.getConnectionId(), tempDestinationIdGenerator.getNextSequenceId());
2060        } else {
2061            dest = new ActiveMQTempQueue(info.getConnectionId(), tempDestinationIdGenerator.getNextSequenceId());
2062        }
2063
2064        DestinationInfo info = new DestinationInfo();
2065        info.setConnectionId(this.info.getConnectionId());
2066        info.setOperationType(DestinationInfo.ADD_OPERATION_TYPE);
2067        info.setDestination(dest);
2068        syncSendPacket(info);
2069
2070        dest.setConnection(this);
2071        activeTempDestinations.put(dest, dest);
2072        return dest;
2073    }
2074
2075    /**
2076     * @param destination
2077     * @throws JMSException
2078     */
2079    public void deleteTempDestination(ActiveMQTempDestination destination) throws JMSException {
2080
2081        checkClosedOrFailed();
2082
2083        for (ActiveMQSession session : this.sessions) {
2084            if (session.isInUse(destination)) {
2085                throw new JMSException("A consumer is consuming from the temporary destination");
2086            }
2087        }
2088
2089        activeTempDestinations.remove(destination);
2090
2091        DestinationInfo destInfo = new DestinationInfo();
2092        destInfo.setConnectionId(this.info.getConnectionId());
2093        destInfo.setOperationType(DestinationInfo.REMOVE_OPERATION_TYPE);
2094        destInfo.setDestination(destination);
2095        destInfo.setTimeout(0);
2096        syncSendPacket(destInfo);
2097    }
2098
2099    public boolean isDeleted(ActiveMQDestination dest) {
2100
2101        // If we are not watching the advisories.. then
2102        // we will assume that the temp destination does exist.
2103        if (advisoryConsumer == null) {
2104            return false;
2105        }
2106
2107        return !activeTempDestinations.containsValue(dest);
2108    }
2109
2110    public boolean isCopyMessageOnSend() {
2111        return copyMessageOnSend;
2112    }
2113
2114    public LongSequenceGenerator getLocalTransactionIdGenerator() {
2115        return localTransactionIdGenerator;
2116    }
2117
2118    public boolean isUseCompression() {
2119        return useCompression;
2120    }
2121
2122    /**
2123     * Enables the use of compression of the message bodies
2124     */
2125    public void setUseCompression(boolean useCompression) {
2126        this.useCompression = useCompression;
2127    }
2128
2129    public void destroyDestination(ActiveMQDestination destination) throws JMSException {
2130
2131        checkClosedOrFailed();
2132        ensureConnectionInfoSent();
2133
2134        DestinationInfo info = new DestinationInfo();
2135        info.setConnectionId(this.info.getConnectionId());
2136        info.setOperationType(DestinationInfo.REMOVE_OPERATION_TYPE);
2137        info.setDestination(destination);
2138        info.setTimeout(0);
2139        syncSendPacket(info);
2140    }
2141
2142    public boolean isDispatchAsync() {
2143        return dispatchAsync;
2144    }
2145
2146    /**
2147     * Enables or disables the default setting of whether or not consumers have
2148     * their messages <a
2149     * href="http://activemq.apache.org/consumer-dispatch-async.html">dispatched
2150     * synchronously or asynchronously by the broker</a>. For non-durable
2151     * topics for example we typically dispatch synchronously by default to
2152     * minimize context switches which boost performance. However sometimes its
2153     * better to go slower to ensure that a single blocked consumer socket does
2154     * not block delivery to other consumers.
2155     *
2156     * @param asyncDispatch If true then consumers created on this connection
2157     *                will default to having their messages dispatched
2158     *                asynchronously. The default value is true.
2159     */
2160    public void setDispatchAsync(boolean asyncDispatch) {
2161        this.dispatchAsync = asyncDispatch;
2162    }
2163
2164    public boolean isObjectMessageSerializationDefered() {
2165        return objectMessageSerializationDefered;
2166    }
2167
2168    /**
2169     * When an object is set on an ObjectMessage, the JMS spec requires the
2170     * object to be serialized by that set method. Enabling this flag causes the
2171     * object to not get serialized. The object may subsequently get serialized
2172     * if the message needs to be sent over a socket or stored to disk.
2173     */
2174    public void setObjectMessageSerializationDefered(boolean objectMessageSerializationDefered) {
2175        this.objectMessageSerializationDefered = objectMessageSerializationDefered;
2176    }
2177
2178    /**
2179     * Unsubscribes a durable subscription that has been created by a client.
2180     * <P>
2181     * This method deletes the state being maintained on behalf of the
2182     * subscriber by its provider.
2183     * <P>
2184     * It is erroneous for a client to delete a durable subscription while there
2185     * is an active <CODE>MessageConsumer </CODE> or
2186     * <CODE>TopicSubscriber</CODE> for the subscription, or while a consumed
2187     * message is part of a pending transaction or has not been acknowledged in
2188     * the session.
2189     *
2190     * @param name the name used to identify this subscription
2191     * @throws JMSException if the session fails to unsubscribe to the durable
2192     *                 subscription due to some internal error.
2193     * @throws InvalidDestinationException if an invalid subscription name is
2194     *                 specified.
2195     * @since 1.1
2196     */
2197    public void unsubscribe(String name) throws InvalidDestinationException, JMSException {
2198        checkClosedOrFailed();
2199        RemoveSubscriptionInfo rsi = new RemoveSubscriptionInfo();
2200        rsi.setConnectionId(getConnectionInfo().getConnectionId());
2201        rsi.setSubscriptionName(name);
2202        rsi.setClientId(getConnectionInfo().getClientId());
2203        syncSendPacket(rsi);
2204    }
2205
2206    /**
2207     * Internal send method optimized: - It does not copy the message - It can
2208     * only handle ActiveMQ messages. - You can specify if the send is async or
2209     * sync - Does not allow you to send /w a transaction.
2210     */
2211    void send(ActiveMQDestination destination, ActiveMQMessage msg, MessageId messageId, int deliveryMode, int priority, long timeToLive, boolean async) throws JMSException {
2212        checkClosedOrFailed();
2213
2214        if (destination.isTemporary() && isDeleted(destination)) {
2215            throw new JMSException("Cannot publish to a deleted Destination: " + destination);
2216        }
2217
2218        msg.setJMSDestination(destination);
2219        msg.setJMSDeliveryMode(deliveryMode);
2220        long expiration = 0L;
2221
2222        if (!isDisableTimeStampsByDefault()) {
2223            long timeStamp = System.currentTimeMillis();
2224            msg.setJMSTimestamp(timeStamp);
2225            if (timeToLive > 0) {
2226                expiration = timeToLive + timeStamp;
2227            }
2228        }
2229
2230        msg.setJMSExpiration(expiration);
2231        msg.setJMSPriority(priority);
2232        msg.setJMSRedelivered(false);
2233        msg.setMessageId(messageId);
2234        msg.onSend();
2235        msg.setProducerId(msg.getMessageId().getProducerId());
2236
2237        if (LOG.isDebugEnabled()) {
2238            LOG.debug("Sending message: " + msg);
2239        }
2240
2241        if (async) {
2242            asyncSendPacket(msg);
2243        } else {
2244            syncSendPacket(msg);
2245        }
2246    }
2247
2248    protected void onConnectionControl(ConnectionControl command) {
2249        if (command.isFaultTolerant()) {
2250            this.optimizeAcknowledge = false;
2251            for (Iterator<ActiveMQSession> i = this.sessions.iterator(); i.hasNext();) {
2252                ActiveMQSession s = i.next();
2253                s.setOptimizeAcknowledge(false);
2254            }
2255        }
2256    }
2257
2258    protected void onConsumerControl(ConsumerControl command) {
2259        if (command.isClose()) {
2260            for (ActiveMQSession session : this.sessions) {
2261                session.close(command.getConsumerId());
2262            }
2263        } else {
2264            for (ActiveMQSession session : this.sessions) {
2265                session.setPrefetchSize(command.getConsumerId(), command.getPrefetch());
2266            }
2267            for (ActiveMQConnectionConsumer connectionConsumer: connectionConsumers) {
2268                ConsumerInfo consumerInfo = connectionConsumer.getConsumerInfo();
2269                if (consumerInfo.getConsumerId().equals(command.getConsumerId())) {
2270                    consumerInfo.setPrefetchSize(command.getPrefetch());
2271                }
2272            }
2273        }
2274    }
2275
2276    protected void transportFailed(IOException error) {
2277        transportFailed.set(true);
2278        if (firstFailureError == null) {
2279            firstFailureError = error;
2280        }
2281    }
2282
2283    /**
2284     * Should a JMS message be copied to a new JMS Message object as part of the
2285     * send() method in JMS. This is enabled by default to be compliant with the
2286     * JMS specification. You can disable it if you do not mutate JMS messages
2287     * after they are sent for a performance boost
2288     */
2289    public void setCopyMessageOnSend(boolean copyMessageOnSend) {
2290        this.copyMessageOnSend = copyMessageOnSend;
2291    }
2292
2293    @Override
2294    public String toString() {
2295        return "ActiveMQConnection {id=" + info.getConnectionId() + ",clientId=" + info.getClientId() + ",started=" + started.get() + "}";
2296    }
2297
2298    protected BlobTransferPolicy createBlobTransferPolicy() {
2299        return new BlobTransferPolicy();
2300    }
2301
2302    public int getProtocolVersion() {
2303        return protocolVersion.get();
2304    }
2305
2306    public int getProducerWindowSize() {
2307        return producerWindowSize;
2308    }
2309
2310    public void setProducerWindowSize(int producerWindowSize) {
2311        this.producerWindowSize = producerWindowSize;
2312    }
2313
2314    public void setAuditDepth(int auditDepth) {
2315        connectionAudit.setAuditDepth(auditDepth);
2316    }
2317
2318    public void setAuditMaximumProducerNumber(int auditMaximumProducerNumber) {
2319        connectionAudit.setAuditMaximumProducerNumber(auditMaximumProducerNumber);
2320    }
2321
2322    protected void removeDispatcher(ActiveMQDispatcher dispatcher) {
2323        connectionAudit.removeDispatcher(dispatcher);
2324    }
2325
2326    protected boolean isDuplicate(ActiveMQDispatcher dispatcher, Message message) {
2327        return checkForDuplicates && connectionAudit.isDuplicate(dispatcher, message);
2328    }
2329
2330    protected void rollbackDuplicate(ActiveMQDispatcher dispatcher, Message message) {
2331        connectionAudit.rollbackDuplicate(dispatcher, message);
2332    }
2333
2334    public IOException getFirstFailureError() {
2335        return firstFailureError;
2336    }
2337
2338    protected void waitForTransportInterruptionProcessingToComplete() throws InterruptedException {
2339        if (!closed.get() && !transportFailed.get() && transportInterruptionProcessingComplete.get()>0) {
2340            LOG.warn("dispatch with outstanding dispatch interruption processing count " + transportInterruptionProcessingComplete.get());
2341            signalInterruptionProcessingComplete();
2342        }
2343    }
2344
2345    protected void transportInterruptionProcessingComplete() {
2346        if (transportInterruptionProcessingComplete.decrementAndGet() == 0) {
2347            signalInterruptionProcessingComplete();
2348        }
2349    }
2350
2351    private void signalInterruptionProcessingComplete() {
2352            if (LOG.isDebugEnabled()) {
2353                LOG.debug("transportInterruptionProcessingComplete: " + transportInterruptionProcessingComplete.get()
2354                        + " for:" + this.getConnectionInfo().getConnectionId());
2355            }
2356
2357            FailoverTransport failoverTransport = transport.narrow(FailoverTransport.class);
2358            if (failoverTransport != null) {
2359                failoverTransport.connectionInterruptProcessingComplete(this.getConnectionInfo().getConnectionId());
2360                if (LOG.isDebugEnabled()) {
2361                    LOG.debug("notified failover transport (" + failoverTransport
2362                            + ") of interruption completion for: " + this.getConnectionInfo().getConnectionId());
2363                }
2364            }
2365            transportInterruptionProcessingComplete.set(0);
2366    }
2367
2368    private void signalInterruptionProcessingNeeded() {
2369        FailoverTransport failoverTransport = transport.narrow(FailoverTransport.class);
2370        if (failoverTransport != null) {
2371            failoverTransport.getStateTracker().transportInterrupted(this.getConnectionInfo().getConnectionId());
2372            if (LOG.isDebugEnabled()) {
2373                LOG.debug("notified failover transport (" + failoverTransport
2374                        + ") of pending interruption processing for: " + this.getConnectionInfo().getConnectionId());
2375            }
2376        }
2377    }
2378
2379    /*
2380     * specify the amount of time in milliseconds that a consumer with a transaction pending recovery
2381     * will wait to receive re dispatched messages.
2382     * default value is 0 so there is no wait by default.
2383     */
2384    public void setConsumerFailoverRedeliveryWaitPeriod(long consumerFailoverRedeliveryWaitPeriod) {
2385        this.consumerFailoverRedeliveryWaitPeriod = consumerFailoverRedeliveryWaitPeriod;
2386    }
2387
2388    public long getConsumerFailoverRedeliveryWaitPeriod() {
2389        return consumerFailoverRedeliveryWaitPeriod;
2390    }
2391
2392    protected Scheduler getScheduler() throws JMSException {
2393        Scheduler result = scheduler;
2394        if (result == null) {
2395            if (isClosing() || isClosed()) {
2396                // without lock contention report the closing state
2397                throw new ConnectionClosedException();
2398            }
2399            synchronized (schedulerLock) {
2400                result = scheduler;
2401                if (result == null) {
2402                    checkClosed();
2403                    try {
2404                        result = new Scheduler("ActiveMQConnection["+info.getConnectionId().getValue()+"] Scheduler");
2405                        result.start();
2406                        scheduler = result;
2407                    } catch(Exception e) {
2408                        throw JMSExceptionSupport.create(e);
2409                    }
2410                }
2411            }
2412        }
2413        return result;
2414    }
2415
2416    protected ThreadPoolExecutor getExecutor() {
2417        return this.executor;
2418    }
2419
2420    protected CopyOnWriteArrayList<ActiveMQSession> getSessions() {
2421        return sessions;
2422    }
2423
2424    /**
2425     * @return the checkForDuplicates
2426     */
2427    public boolean isCheckForDuplicates() {
2428        return this.checkForDuplicates;
2429    }
2430
2431    /**
2432     * @param checkForDuplicates the checkForDuplicates to set
2433     */
2434    public void setCheckForDuplicates(boolean checkForDuplicates) {
2435        this.checkForDuplicates = checkForDuplicates;
2436    }
2437
2438    public boolean isTransactedIndividualAck() {
2439        return transactedIndividualAck;
2440    }
2441
2442    public void setTransactedIndividualAck(boolean transactedIndividualAck) {
2443        this.transactedIndividualAck = transactedIndividualAck;
2444    }
2445
2446    public boolean isNonBlockingRedelivery() {
2447        return nonBlockingRedelivery;
2448    }
2449
2450    public void setNonBlockingRedelivery(boolean nonBlockingRedelivery) {
2451        this.nonBlockingRedelivery = nonBlockingRedelivery;
2452    }
2453
2454    public boolean isRmIdFromConnectionId() {
2455        return rmIdFromConnectionId;
2456    }
2457
2458    public void setRmIdFromConnectionId(boolean rmIdFromConnectionId) {
2459        this.rmIdFromConnectionId = rmIdFromConnectionId;
2460    }
2461
2462    /**
2463     * Removes any TempDestinations that this connection has cached, ignoring
2464     * any exceptions generated because the destination is in use as they should
2465     * not be removed.
2466     * Used from a pooled connection, b/c it will not be explicitly closed.
2467     */
2468    public void cleanUpTempDestinations() {
2469
2470        if (this.activeTempDestinations == null || this.activeTempDestinations.isEmpty()) {
2471            return;
2472        }
2473
2474        Iterator<ConcurrentMap.Entry<ActiveMQTempDestination, ActiveMQTempDestination>> entries
2475            = this.activeTempDestinations.entrySet().iterator();
2476        while(entries.hasNext()) {
2477            ConcurrentMap.Entry<ActiveMQTempDestination, ActiveMQTempDestination> entry = entries.next();
2478            try {
2479                // Only delete this temp destination if it was created from this connection. The connection used
2480                // for the advisory consumer may also have a reference to this temp destination.
2481                ActiveMQTempDestination dest = entry.getValue();
2482                String thisConnectionId = (info.getConnectionId() == null) ? "" : info.getConnectionId().toString();
2483                if (dest.getConnectionId() != null && dest.getConnectionId().equals(thisConnectionId)) {
2484                    this.deleteTempDestination(entry.getValue());
2485                }
2486            } catch (Exception ex) {
2487                // the temp dest is in use so it can not be deleted.
2488                // it is ok to leave it to connection tear down phase
2489            }
2490        }
2491    }
2492
2493    /**
2494     * Sets the Connection wide RedeliveryPolicyMap for handling messages that are being rolled back.
2495     * @param redeliveryPolicyMap the redeliveryPolicyMap to set
2496     */
2497    public void setRedeliveryPolicyMap(RedeliveryPolicyMap redeliveryPolicyMap) {
2498        this.redeliveryPolicyMap = redeliveryPolicyMap;
2499    }
2500
2501    /**
2502     * Gets the Connection's configured RedeliveryPolicyMap which will be used by all the
2503     * Consumers when dealing with transaction messages that have been rolled back.
2504     *
2505     * @return the redeliveryPolicyMap
2506     */
2507    public RedeliveryPolicyMap getRedeliveryPolicyMap() {
2508        return redeliveryPolicyMap;
2509    }
2510
2511    public int getMaxThreadPoolSize() {
2512        return maxThreadPoolSize;
2513    }
2514
2515    public void setMaxThreadPoolSize(int maxThreadPoolSize) {
2516        this.maxThreadPoolSize = maxThreadPoolSize;
2517    }
2518
2519    /**
2520     * Enable enforcement of QueueConnection semantics.
2521     *
2522     * @return this object, useful for chaining
2523     */
2524    ActiveMQConnection enforceQueueOnlyConnection() {
2525        this.queueOnlyConnection = true;
2526        return this;
2527    }
2528
2529    public RejectedExecutionHandler getRejectedTaskHandler() {
2530        return rejectedTaskHandler;
2531    }
2532
2533    public void setRejectedTaskHandler(RejectedExecutionHandler rejectedTaskHandler) {
2534        this.rejectedTaskHandler = rejectedTaskHandler;
2535    }
2536
2537    /**
2538     * Gets the configured time interval that is used to force all MessageConsumers that have optimizedAcknowledge enabled
2539     * to send an ack for any outstanding Message Acks.  By default this value is set to zero meaning that the consumers
2540     * will not do any background Message acknowledgment.
2541     *
2542     * @return the scheduledOptimizedAckInterval
2543     */
2544    public long getOptimizedAckScheduledAckInterval() {
2545        return optimizedAckScheduledAckInterval;
2546    }
2547
2548    /**
2549     * Sets the amount of time between scheduled sends of any outstanding Message Acks for consumers that
2550     * have been configured with optimizeAcknowledge enabled.
2551     *
2552     * @param optimizedAckScheduledAckInterval the scheduledOptimizedAckInterval to set
2553     */
2554    public void setOptimizedAckScheduledAckInterval(long optimizedAckScheduledAckInterval) {
2555        this.optimizedAckScheduledAckInterval = optimizedAckScheduledAckInterval;
2556    }
2557
2558    /**
2559     * @return true if MessageConsumer instance will check for expired messages before dispatch.
2560     */
2561    public boolean isConsumerExpiryCheckEnabled() {
2562        return consumerExpiryCheckEnabled;
2563    }
2564
2565    /**
2566     * Controls whether message expiration checking is done in each MessageConsumer
2567     * prior to dispatching a message.  Disabling this check can lead to consumption
2568     * of expired messages.
2569     *
2570     * @param consumerExpiryCheckEnabled
2571     *        controls whether expiration checking is done prior to dispatch.
2572     */
2573    public void setConsumerExpiryCheckEnabled(boolean consumerExpiryCheckEnabled) {
2574        this.consumerExpiryCheckEnabled = consumerExpiryCheckEnabled;
2575    }
2576
2577    public List<String> getTrustedPackages() {
2578        return trustedPackages;
2579    }
2580
2581    public void setTrustedPackages(List<String> trustedPackages) {
2582        this.trustedPackages = trustedPackages;
2583    }
2584
2585    public boolean isTrustAllPackages() {
2586        return trustAllPackages;
2587    }
2588
2589    public void setTrustAllPackages(boolean trustAllPackages) {
2590        this.trustAllPackages = trustAllPackages;
2591    }
2592
2593    public int getConnectResponseTimeout() {
2594        return connectResponseTimeout;
2595    }
2596
2597    public void setConnectResponseTimeout(int connectResponseTimeout) {
2598        this.connectResponseTimeout = connectResponseTimeout;
2599    }
2600}