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