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