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