content: /*
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements. See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to You under the Apache License, Version 2.0
   * (the "License"); you may not use this file except in compliance with
   * the License. You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package org.apache.activemq.artemis.jms.client;
  
  import javax.jms.Connection;
  import javax.jms.ConnectionFactory;
  import javax.jms.JMSContext;
  import javax.jms.JMSException;
  import javax.jms.JMSRuntimeException;
  import javax.jms.JMSSecurityException;
  import javax.jms.JMSSecurityRuntimeException;
  import javax.jms.QueueConnection;
  import javax.jms.TopicConnection;
  import javax.jms.XAConnection;
  import javax.jms.XAConnectionFactory;
  import javax.jms.XAJMSContext;
  import javax.jms.XAQueueConnection;
  import javax.jms.XATopicConnection;
  import javax.naming.Context;
  import java.io.Externalizable;
  import java.io.IOException;
  import java.io.InvalidObjectException;
  import java.io.ObjectInput;
  import java.io.ObjectOutput;
  import java.lang.reflect.InvocationTargetException;
  import java.net.URI;
  import java.security.AccessController;
  import java.security.PrivilegedAction;
  import java.util.Map;
  import java.util.Properties;
  
  import org.apache.activemq.artemis.api.core.DiscoveryGroupConfiguration;
  import org.apache.activemq.artemis.api.core.Interceptor;
  import org.apache.activemq.artemis.api.core.TransportConfiguration;
  import org.apache.activemq.artemis.api.core.UDPBroadcastEndpointFactory;
  import org.apache.activemq.artemis.api.core.client.ActiveMQClient;
  import org.apache.activemq.artemis.api.core.client.ClientSessionFactory;
  import org.apache.activemq.artemis.api.core.client.ServerLocator;
  import org.apache.activemq.artemis.api.jms.ActiveMQJMSClient;
  import org.apache.activemq.artemis.api.jms.ActiveMQJMSConstants;
  import org.apache.activemq.artemis.api.jms.JMSFactoryType;
  import org.apache.activemq.artemis.core.client.impl.ServerLocatorImpl;
  import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants;
  import org.apache.activemq.artemis.jndi.JNDIStorable;
  import org.apache.activemq.artemis.spi.core.remoting.ClientProtocolManagerFactory;
  import org.apache.activemq.artemis.uri.ConnectionFactoryParser;
  import org.apache.activemq.artemis.uri.ServerLocatorParser;
  import org.apache.activemq.artemis.utils.ClassloadingUtil;
  import org.apache.activemq.artemis.utils.uri.BeanSupport;
  import org.apache.activemq.artemis.utils.uri.URISupport;
  
  /**
   * ActiveMQ Artemis implementation of a JMS ConnectionFactory.
   * <p>
   * This connection factory will use defaults defined by {@link DefaultConnectionProperties}.
   */
  public class ActiveMQConnectionFactory extends JNDIStorable implements ConnectionFactoryOptions, Externalizable, ConnectionFactory, XAConnectionFactory, AutoCloseable {
  
     private static final long serialVersionUID = 6730844785641767519L;
  
     private ServerLocator serverLocator;
  
     private String clientID;
  
     private boolean enableSharedClientID = ActiveMQClient.DEFAULT_ENABLED_SHARED_CLIENT_ID;
  
     private int dupsOKBatchSize = ActiveMQClient.DEFAULT_ACK_BATCH_SIZE;
  
     private int transactionBatchSize = ActiveMQClient.DEFAULT_ACK_BATCH_SIZE;
  
     private boolean readOnly;
  
     private String user;
  
     private String password;
  
     private String protocolManagerFactoryStr;
  
     private String deserializationDenyList;
  
     private String deserializationAllowList;
  
     private boolean cacheDestinations;
  
     private boolean ignoreJTA;
  
     private boolean enable1xPrefixes = ActiveMQJMSClient.DEFAULT_ENABLE_1X_PREFIXES;
  
     // keeping this field for serialization compatibility only. do not use it
     @SuppressWarnings("unused")
     private boolean finalizeCheck;
  
     @Override
     public void writeExternal(ObjectOutput out) throws IOException {
        URI uri = toURI();
  
        try {
           out.writeUTF(uri.toASCIIString());
        } catch (Exception e) {
           if (e instanceof IOException ioException) {
              throw ioException;
           }
           throw new IOException(e);
        }
     }
  
     public URI toURI() throws IOException {
        ConnectionFactoryParser parser = new ConnectionFactoryParser();
        String scheme;
        if (serverLocator.getDiscoveryGroupConfiguration() != null) {
           if (serverLocator.getDiscoveryGroupConfiguration().getBroadcastEndpointFactory() instanceof UDPBroadcastEndpointFactory) {
              scheme = "udp";
           } else {
              scheme = "jgroups";
           }
        } else {
           if (serverLocator.allInVM()) {
              scheme = "vm";
           } else {
              scheme = "tcp";
           }
        }
  
        URI uri;
  
        try {
           uri = parser.createSchema(scheme, this);
        } catch (Exception e) {
           if (e instanceof IOException ioException) {
              throw ioException;
           }
           throw new IOException(e);
        }
        return uri;
     }
  
     public String getProtocolManagerFactoryStr() {
        return protocolManagerFactoryStr;
     }
  
     public void setProtocolManagerFactoryStr(final String protocolManagerFactoryStr) {
  
        if (protocolManagerFactoryStr != null && !protocolManagerFactoryStr.trim().isEmpty() &&
           !protocolManagerFactoryStr.equals("undefined")) {
           AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
              ClientProtocolManagerFactory protocolManagerFactory = (ClientProtocolManagerFactory) ClassloadingUtil.newInstanceFromClassLoader(ActiveMQConnectionFactory.class, protocolManagerFactoryStr, ClientProtocolManagerFactory.class);
              serverLocator.setProtocolManagerFactory(protocolManagerFactory);
              return null;
           });
  
           this.protocolManagerFactoryStr = protocolManagerFactoryStr;
        }
     }
  
     @Override
     @Deprecated(forRemoval = true)
     public String getDeserializationBlackList() {
        return deserializationDenyList;
     }
  
     @Override
     @Deprecated(forRemoval = true)
     public void setDeserializationBlackList(String denyList) {
        this.deserializationDenyList = denyList;
     }
  
     @Override
     @Deprecated(forRemoval = true)
     public String getDeserializationWhiteList() {
        return deserializationAllowList;
     }
  
     @Override
     @Deprecated(forRemoval = true)
     public void setDeserializationWhiteList(String allowList) {
        this.deserializationAllowList = allowList;
     }
  
     @Override
     public String getDeserializationDenyList() {
        return deserializationDenyList;
     }
  
     @Override
     public void setDeserializationDenyList(String denyList) {
        this.deserializationDenyList = denyList;
     }
  
     @Override
     public String getDeserializationAllowList() {
        return deserializationAllowList;
     }
  
     @Override
     public void setDeserializationAllowList(String allowList) {
        this.deserializationAllowList = allowList;
     }
  
     @Override
     public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        String url = in.readUTF();
        ConnectionFactoryParser parser = new ConnectionFactoryParser();
        ServerLocatorParser locatorParser = new ServerLocatorParser();
        try {
           URI uri = new URI(url);
           serverLocator = locatorParser.newObject(uri, null);
           parser.populateObject(uri, this);
        } catch (Exception e) {
           InvalidObjectException ex = new InvalidObjectException(e.getMessage());
           ex.initCause(e);
           throw ex;
        }
     }
  
     /**
      * This will use a default URI from {@link DefaultConnectionProperties}
      */
     public ActiveMQConnectionFactory() {
        this(DefaultConnectionProperties.DEFAULT_BROKER_URL);
     }
  
     public ActiveMQConnectionFactory(String brokerURL) {
        try {
           setBrokerURL(brokerURL);
        } catch (Throwable e) {
           throw new IllegalStateException(e);
        }
     }
  
     /**
      * <b>Warning</b>: This method will not clear any previous properties. For example, if you set the user first then
      * you change the brokerURL without passing the user. The previously set user will still exist, and nothing will
      * clear it out.
      * <p>
      * Also, you cannot use this method after this {@code ConnectionFactory} is made {@code readOnly} which happens after
      * the first time it's used to create a connection.
      */
     public void setBrokerURL(String brokerURL) throws JMSException {
        if (readOnly) {
           throw new javax.jms.IllegalStateException("You cannot use setBrokerURL after the connection factory has been used");
        }
        ConnectionFactoryParser cfParser = new ConnectionFactoryParser();
        try {
           URI uri = cfParser.expandURI(brokerURL);
           serverLocator = ServerLocatorImpl.newLocator(uri);
           cfParser.populateObject(uri, this);
        } catch (Exception e) {
           throw new RuntimeException(e.getMessage(), e);
        }
  
        if (getUser() == null) {
           setUser(DefaultConnectionProperties.DEFAULT_USER);
        }
  
        if (getPassword() == null) {
           setPassword(DefaultConnectionProperties.DEFAULT_PASSWORD);
        }
  
        if (getPasswordCodec() == null) {
           setPasswordCodec(DefaultConnectionProperties.DEFAULT_PASSWORD_CODEC);
        }
     }
  
     /**
      * For compatibility and users used to this kind of constructor
      */
     public ActiveMQConnectionFactory(String url, String user, String password) {
        this(url);
        setUser(user).setPassword(password);
     }
  
     public ActiveMQConnectionFactory(final ServerLocator serverLocator) {
        this.serverLocator = serverLocator;
     }
  
     public ActiveMQConnectionFactory(final boolean ha, final DiscoveryGroupConfiguration groupConfiguration) {
        if (ha) {
           serverLocator = ActiveMQClient.createServerLocatorWithHA(groupConfiguration);
        } else {
           serverLocator = ActiveMQClient.createServerLocatorWithoutHA(groupConfiguration);
        }
     }
  
     public ActiveMQConnectionFactory(final boolean ha, final TransportConfiguration... initialConnectors) {
        if (ha) {
           serverLocator = ActiveMQClient.createServerLocatorWithHA(initialConnectors);
        } else {
           serverLocator = ActiveMQClient.createServerLocatorWithoutHA(initialConnectors);
        }
     }
  
     @Override
     public Connection createConnection() throws JMSException {
        return createConnection(user, password);
     }
  
     @Override
     public Connection createConnection(final String username, final String password) throws JMSException {
        return createConnectionInternal(username, password, false, ActiveMQConnection.TYPE_GENERIC_CONNECTION);
     }
  
     @Override
     public JMSContext createContext() {
        return createContext(user, password);
     }
  
     @Override
     public JMSContext createContext(final int sessionMode) {
        return createContext(user, password, sessionMode);
     }
  
     @Override
     public JMSContext createContext(final String userName, final String password) {
        return createContext(userName, password, JMSContext.AUTO_ACKNOWLEDGE);
     }
  
     @Override
     public JMSContext createContext(String userName, String password, int sessionMode) {
        validateSessionMode(sessionMode);
        try {
           ActiveMQConnection connection = createConnectionInternal(userName, password, false, ActiveMQConnection.TYPE_GENERIC_CONNECTION);
           return connection.createContext(sessionMode);
        } catch (JMSSecurityException e) {
           throw new JMSSecurityRuntimeException(e.getMessage(), e.getErrorCode(), e);
        } catch (JMSException e) {
           throw JmsExceptionUtils.convertToRuntimeException(e);
        }
     }
  
     private static void validateSessionMode(int mode) {
        switch (mode) {
           case JMSContext.AUTO_ACKNOWLEDGE:
           case JMSContext.CLIENT_ACKNOWLEDGE:
           case JMSContext.DUPS_OK_ACKNOWLEDGE:
           case JMSContext.SESSION_TRANSACTED:
           case ActiveMQJMSConstants.PRE_ACKNOWLEDGE:
           case ActiveMQJMSConstants.INDIVIDUAL_ACKNOWLEDGE: {
              return;
           }
           default:
              throw new JMSRuntimeException("Invalid Session Mode: " + mode);
        }
     }
  
     public QueueConnection createQueueConnection() throws JMSException {
        return createQueueConnection(user, password);
     }
  
     public QueueConnection createQueueConnection(final String username, final String password) throws JMSException {
        return createConnectionInternal(username, password, false, ActiveMQConnection.TYPE_QUEUE_CONNECTION);
     }
  
     // TopicConnectionFactory implementation --------------------------------------------------------
  
     public TopicConnection createTopicConnection() throws JMSException {
        return createTopicConnection(user, password);
     }
  
     public TopicConnection createTopicConnection(final String username, final String password) throws JMSException {
        return createConnectionInternal(username, password, false, ActiveMQConnection.TYPE_TOPIC_CONNECTION);
     }
  
     // XAConnectionFactory implementation -----------------------------------------------------------
  
     @Override
     public XAConnection createXAConnection() throws JMSException {
        return createXAConnection(user, password);
     }
  
     @Override
     public XAConnection createXAConnection(final String username, final String password) throws JMSException {
        return (XAConnection) createConnectionInternal(username, password, true, ActiveMQConnection.TYPE_GENERIC_CONNECTION);
     }
  
     @Override
     public XAJMSContext createXAContext() {
        return createXAContext(user, password);
     }
  
     @Override
     public XAJMSContext createXAContext(String userName, String password) {
        try {
           ActiveMQConnection connection = createConnectionInternal(userName, password, true, ActiveMQConnection.TYPE_GENERIC_CONNECTION);
           return connection.createXAContext();
        } catch (JMSSecurityException e) {
           throw new JMSSecurityRuntimeException(e.getMessage(), e.getErrorCode(), e);
        } catch (JMSException e) {
           throw JmsExceptionUtils.convertToRuntimeException(e);
        }
     }
  
     // XAQueueConnectionFactory implementation ------------------------------------------------------
  
     public XAQueueConnection createXAQueueConnection() throws JMSException {
        return createXAQueueConnection(user, password);
     }
  
     public XAQueueConnection createXAQueueConnection(final String username, final String password) throws JMSException {
        return (XAQueueConnection) createConnectionInternal(username, password, true, ActiveMQConnection.TYPE_QUEUE_CONNECTION);
     }
  
     // XATopicConnectionFactory implementation ------------------------------------------------------
  
     public XATopicConnection createXATopicConnection() throws JMSException {
        return createXATopicConnection(user, password);
     }
  
     public XATopicConnection createXATopicConnection(final String username, final String password) throws JMSException {
        return (XATopicConnection) createConnectionInternal(username, password, true, ActiveMQConnection.TYPE_TOPIC_CONNECTION);
     }
  
     @Override
     protected void buildFromProperties(Properties props) {
        String url = props.getProperty(Context.PROVIDER_URL);
        if (url == null || url.isEmpty()) {
           url = props.getProperty("brokerURL");
        }
  
        if (url == null || url.isEmpty()) {
           throw new IllegalArgumentException(Context.PROVIDER_URL + " or " + "brokerURL is required");
        }
        try {
           if (url != null && !url.isEmpty()) {
              url = updateBrokerURL(url, props);
              setBrokerURL(url);
           }
  
           BeanSupport.setProperties(this, props);
        } catch (JMSException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
           throw new RuntimeException(e);
        }
     }
  
     private String updateBrokerURL(String url, Properties props) {
        ConnectionFactoryParser cfParser = new ConnectionFactoryParser();
        try {
           URI uri = cfParser.expandURI(url);
           final Map<String, String> params = URISupport.parseParameters(uri);
  
           for (String key : TransportConstants.ALLOWABLE_CONNECTOR_KEYS) {
              final String val = props.getProperty(key);
              if (val != null) {
                 params.put(key, val);
              }
           }
  
           final String newUrl = URISupport.applyParameters(uri, params).toString();
           return newUrl;
        } catch (Exception e) {
           throw new RuntimeException(e.getMessage(), e);
        }
     }
  
     @Override
     protected void populateProperties(Properties props) {
        try {
           URI uri = toURI();
           if (uri != null) {
              props.put(Context.PROVIDER_URL, uri.toASCIIString());
              props.put("brokerURL", uri.toASCIIString());
           }
           BeanSupport.getProperties(this, props);
        } catch (IOException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
           throw new RuntimeException(e);
        }
     }
  
     public boolean isHA() {
        return serverLocator.isHA();
     }
  
     public synchronized String getConnectionLoadBalancingPolicyClassName() {
        return serverLocator.getConnectionLoadBalancingPolicyClassName();
     }
  
     public synchronized void setConnectionLoadBalancingPolicyClassName(final String connectionLoadBalancingPolicyClassName) {
        checkWrite();
        serverLocator.setConnectionLoadBalancingPolicyClassName(connectionLoadBalancingPolicyClassName);
     }
  
     public synchronized TransportConfiguration[] getStaticConnectors() {
        return serverLocator.getStaticTransportConfigurations();
     }
  
     public synchronized DiscoveryGroupConfiguration getDiscoveryGroupConfiguration() {
        return serverLocator.getDiscoveryGroupConfiguration();
     }
  
     public synchronized String getClientID() {
        return clientID;
     }
  
     public synchronized void setClientID(final String clientID) {
        checkWrite();
        this.clientID = clientID;
     }
  
     public boolean isEnableSharedClientID() {
        return enableSharedClientID;
     }
  
     public void setEnableSharedClientID(boolean enableSharedClientID) {
        this.enableSharedClientID = enableSharedClientID;
     }
  
     public synchronized int getDupsOKBatchSize() {
        return dupsOKBatchSize;
     }
  
     public synchronized void setDupsOKBatchSize(final int dupsOKBatchSize) {
        checkWrite();
        this.dupsOKBatchSize = dupsOKBatchSize;
     }
  
     public synchronized int getTransactionBatchSize() {
        return transactionBatchSize;
     }
  
     public synchronized void setTransactionBatchSize(final int transactionBatchSize) {
        checkWrite();
        this.transactionBatchSize = transactionBatchSize;
     }
  
     public synchronized boolean isCacheDestinations() {
        return this.cacheDestinations;
     }
  
     public synchronized void setCacheDestinations(final boolean cacheDestinations) {
        checkWrite();
        this.cacheDestinations = cacheDestinations;
     }
  
     public synchronized boolean isEnable1xPrefixes() {
        return this.enable1xPrefixes;
     }
  
     public synchronized void setEnable1xPrefixes(final boolean enable1xPrefixes) {
        checkWrite();
        this.enable1xPrefixes = enable1xPrefixes;
     }
  
     public synchronized long getClientFailureCheckPeriod() {
        return serverLocator.getClientFailureCheckPeriod();
     }
  
     public synchronized void setClientFailureCheckPeriod(final long clientFailureCheckPeriod) {
        checkWrite();
        serverLocator.setClientFailureCheckPeriod(clientFailureCheckPeriod);
     }
  
     public synchronized long getConnectionTTL() {
        return serverLocator.getConnectionTTL();
     }
  
     public synchronized void setConnectionTTL(final long connectionTTL) {
        checkWrite();
        serverLocator.setConnectionTTL(connectionTTL);
     }
  
     public synchronized long getCallTimeout() {
        return serverLocator.getCallTimeout();
     }
  
     public synchronized void setCallTimeout(final long callTimeout) {
        checkWrite();
        serverLocator.setCallTimeout(callTimeout);
     }
  
     public synchronized long getCallFailoverTimeout() {
        return serverLocator.getCallFailoverTimeout();
     }
  
     public synchronized void setCallFailoverTimeout(final long callTimeout) {
        checkWrite();
        serverLocator.setCallFailoverTimeout(callTimeout);
     }
  
     public synchronized void setUseTopologyForLoadBalancing(boolean useTopologyForLoadBalancing) {
        checkWrite();
        serverLocator.setUseTopologyForLoadBalancing(useTopologyForLoadBalancing);
     }
  
     public synchronized boolean isUseTopologyForLoadBalancing() {
        return serverLocator.getUseTopologyForLoadBalancing();
     }
  
     public synchronized int getConsumerWindowSize() {
        return serverLocator.getConsumerWindowSize();
     }
  
     public synchronized void setConsumerWindowSize(final int consumerWindowSize) {
        checkWrite();
        serverLocator.setConsumerWindowSize(consumerWindowSize);
     }
  
     public synchronized int getConsumerMaxRate() {
        return serverLocator.getConsumerMaxRate();
     }
  
     public synchronized void setConsumerMaxRate(final int consumerMaxRate) {
        checkWrite();
        serverLocator.setConsumerMaxRate(consumerMaxRate);
     }
  
     public synchronized int getConfirmationWindowSize() {
        return serverLocator.getConfirmationWindowSize();
     }
  
     public synchronized void setConfirmationWindowSize(final int confirmationWindowSize) {
        checkWrite();
        serverLocator.setConfirmationWindowSize(confirmationWindowSize);
     }
  
     public synchronized int getProducerMaxRate() {
        return serverLocator.getProducerMaxRate();
     }
  
     public synchronized void setProducerMaxRate(final int producerMaxRate) {
        checkWrite();
        serverLocator.setProducerMaxRate(producerMaxRate);
     }
  
     public synchronized int getProducerWindowSize() {
        return serverLocator.getProducerWindowSize();
     }
  
     public synchronized void setProducerWindowSize(final int producerWindowSize) {
        checkWrite();
        serverLocator.setProducerWindowSize(producerWindowSize);
     }
  
     public synchronized void setCacheLargeMessagesClient(final boolean cacheLargeMessagesClient) {
        checkWrite();
        serverLocator.setCacheLargeMessagesClient(cacheLargeMessagesClient);
     }
  
     public synchronized boolean isCacheLargeMessagesClient() {
        return serverLocator.isCacheLargeMessagesClient();
     }
  
     public synchronized int getMinLargeMessageSize() {
        return serverLocator.getMinLargeMessageSize();
     }
  
     public synchronized void setMinLargeMessageSize(final int minLargeMessageSize) {
        checkWrite();
        serverLocator.setMinLargeMessageSize(minLargeMessageSize);
     }
  
     public synchronized boolean isBlockOnAcknowledge() {
        return serverLocator.isBlockOnAcknowledge();
     }
  
     public synchronized void setBlockOnAcknowledge(final boolean blockOnAcknowledge) {
        checkWrite();
        serverLocator.setBlockOnAcknowledge(blockOnAcknowledge);
     }
  
     public synchronized boolean isBlockOnNonDurableSend() {
        return serverLocator.isBlockOnNonDurableSend();
     }
  
     public synchronized void setBlockOnNonDurableSend(final boolean blockOnNonDurableSend) {
        checkWrite();
        serverLocator.setBlockOnNonDurableSend(blockOnNonDurableSend);
     }
  
     public synchronized boolean isBlockOnDurableSend() {
        return serverLocator.isBlockOnDurableSend();
     }
  
     public synchronized void setBlockOnDurableSend(final boolean blockOnDurableSend) {
        checkWrite();
        serverLocator.setBlockOnDurableSend(blockOnDurableSend);
     }
  
     public synchronized boolean isAutoGroup() {
        return serverLocator.isAutoGroup();
     }
  
     public synchronized void setAutoGroup(final boolean autoGroup) {
        checkWrite();
        serverLocator.setAutoGroup(autoGroup);
     }
  
     public synchronized boolean isPreAcknowledge() {
        return serverLocator.isPreAcknowledge();
     }
  
     public synchronized void setPreAcknowledge(final boolean preAcknowledge) {
        checkWrite();
        serverLocator.setPreAcknowledge(preAcknowledge);
     }
  
     public synchronized long getRetryInterval() {
        return serverLocator.getRetryInterval();
     }
  
     public synchronized void setRetryInterval(final long retryInterval) {
        checkWrite();
        serverLocator.setRetryInterval(retryInterval);
     }
  
     public synchronized long getMaxRetryInterval() {
        return serverLocator.getMaxRetryInterval();
     }
  
     public synchronized void setMaxRetryInterval(final long retryInterval) {
        checkWrite();
        serverLocator.setMaxRetryInterval(retryInterval);
     }
  
     public synchronized double getRetryIntervalMultiplier() {
        return serverLocator.getRetryIntervalMultiplier();
     }
  
     public synchronized void setRetryIntervalMultiplier(final double retryIntervalMultiplier) {
        checkWrite();
        serverLocator.setRetryIntervalMultiplier(retryIntervalMultiplier);
     }
  
     public synchronized int getReconnectAttempts() {
        return serverLocator.getReconnectAttempts();
     }
  
     public synchronized void setReconnectAttempts(final int reconnectAttempts) {
        checkWrite();
        serverLocator.setReconnectAttempts(reconnectAttempts);
     }
  
     public synchronized void setInitialConnectAttempts(final int reconnectAttempts) {
        checkWrite();
        serverLocator.setInitialConnectAttempts(reconnectAttempts);
     }
  
     public synchronized int getInitialConnectAttempts() {
        return serverLocator.getInitialConnectAttempts();
     }
  
     @Deprecated
     public synchronized boolean isFailoverOnInitialConnection() {
        return false;
     }
  
     @Deprecated
     public synchronized void setFailoverOnInitialConnection(final boolean failover) {
     }
  
     public synchronized boolean isUseGlobalPools() {
        return serverLocator.isUseGlobalPools();
     }
  
     public synchronized void setUseGlobalPools(final boolean useGlobalPools) {
        checkWrite();
        serverLocator.setUseGlobalPools(useGlobalPools);
     }
  
     public synchronized int getScheduledThreadPoolMaxSize() {
        return serverLocator.getScheduledThreadPoolMaxSize();
     }
  
     public synchronized void setScheduledThreadPoolMaxSize(final int scheduledThreadPoolMaxSize) {
        checkWrite();
        serverLocator.setScheduledThreadPoolMaxSize(scheduledThreadPoolMaxSize);
     }
  
     public synchronized int getThreadPoolMaxSize() {
        return serverLocator.getThreadPoolMaxSize();
     }
  
     public synchronized void setThreadPoolMaxSize(final int threadPoolMaxSize) {
        checkWrite();
        serverLocator.setThreadPoolMaxSize(threadPoolMaxSize);
     }
  
     public synchronized int getInitialMessagePacketSize() {
        return serverLocator.getInitialMessagePacketSize();
     }
  
     public synchronized void setInitialMessagePacketSize(final int size) {
        checkWrite();
        serverLocator.setInitialMessagePacketSize(size);
     }
  
     public boolean isIgnoreJTA() {
        return ignoreJTA;
     }
  
     public void setIgnoreJTA(boolean ignoreJTA) {
        checkWrite();
        this.ignoreJTA = ignoreJTA;
     }
  
     /**
      * Set the list of {@link Interceptor}s to use for incoming packets.
      *
      * @param interceptorList a comma separated string of incoming interceptor class names to be used. Each interceptor
      *                        needs a default Constructor to be used with this method.
      */
     public void setIncomingInterceptorList(String interceptorList) {
        checkWrite();
        serverLocator.setIncomingInterceptorList(interceptorList);
     }
  
     public String getIncomingInterceptorList() {
        return serverLocator.getIncomingInterceptorList();
     }
  
     /**
      * Set the list of {@link Interceptor}s to use for outgoing packets.
      *
      * @param interceptorList a comma separated string of incoming interceptor class names to be used. Each interceptor
      *                        needs a default Constructor to be used with this method.
      */
     public void setOutgoingInterceptorList(String interceptorList) {
        serverLocator.setOutgoingInterceptorList(interceptorList);
     }
  
     public String getOutgoingInterceptorList() {
        return serverLocator.getOutgoingInterceptorList();
     }
  
     public ActiveMQConnectionFactory setUser(String user) {
        checkWrite();
        this.user = user;
        return this;
     }
  
     public String getUser() {
        return user;
     }
  
     public String getPassword() {
        return password;
     }
  
     public ActiveMQConnectionFactory setPassword(String password) {
        checkWrite();
        this.password = password;
        return this;
     }
  
     public String getPasswordCodec() {
        return serverLocator.getPasswordCodec();
     }
  
     public ActiveMQConnectionFactory setPasswordCodec(String passwordCodec) {
        checkWrite();
        serverLocator.setPasswordCodec(passwordCodec);
        return this;
     }
  
     public void setGroupID(final String groupID) {
        serverLocator.setGroupID(groupID);
     }
  
     public String getGroupID() {
        return serverLocator.getGroupID();
     }
  
     public boolean isCompressLargeMessage() {
        return serverLocator.isCompressLargeMessage();
     }
  
     public void setCompressLargeMessage(boolean avoidLargeMessages) {
        serverLocator.setCompressLargeMessage(avoidLargeMessages);
     }
  
     public int getCompressionLevel() {
        return serverLocator.getCompressionLevel();
     }
  
     public void setCompressionLevel(int compressionLevel) {
        serverLocator.setCompressionLevel(compressionLevel);
     }
  
     @Override
     public void close() {
        ServerLocator locator0 = serverLocator;
        if (locator0 != null)
           locator0.close();
     }
  
     public ServerLocator getServerLocator() {
        return serverLocator;
     }
  
     public int getFactoryType() {
        return JMSFactoryType.CF.intValue();
     }
  
     protected synchronized ActiveMQConnection createConnectionInternal(final String username,
                                                                        final String password,
                                                                        final boolean isXA,
                                                                        final int type) throws JMSException {
        makeReadOnly();
  
        ClientSessionFactory factory;
  
        try {
           factory = serverLocator.createSessionFactory();
        } catch (Exception e) {
           JMSException jmse = new JMSException("Failed to create session factory");
  
           jmse.initCause(e);
           jmse.setLinkedException(e);
  
           throw jmse;
        }
  
        ActiveMQConnection connection = null;
  
        if (isXA) {
           if (type == ActiveMQConnection.TYPE_GENERIC_CONNECTION) {
              connection = new ActiveMQXAConnection(this, username, password, type, clientID, dupsOKBatchSize, transactionBatchSize, cacheDestinations, enable1xPrefixes, factory);
           } else if (type == ActiveMQConnection.TYPE_QUEUE_CONNECTION) {
              connection = new ActiveMQXAConnection(this, username, password, type, clientID, dupsOKBatchSize, transactionBatchSize, cacheDestinations, enable1xPrefixes, factory);
           } else if (type == ActiveMQConnection.TYPE_TOPIC_CONNECTION) {
              connection = new ActiveMQXAConnection(this, username, password, type, clientID, dupsOKBatchSize, transactionBatchSize, cacheDestinations, enable1xPrefixes, factory);
           }
        } else {
           if (type == ActiveMQConnection.TYPE_GENERIC_CONNECTION) {
              connection = new ActiveMQConnection(this, username, password, type, clientID, dupsOKBatchSize, transactionBatchSize, cacheDestinations, enable1xPrefixes, factory);
           } else if (type == ActiveMQConnection.TYPE_QUEUE_CONNECTION) {
              connection = new ActiveMQConnection(this, username, password, type, clientID, dupsOKBatchSize, transactionBatchSize, cacheDestinations, enable1xPrefixes, factory);
           } else if (type == ActiveMQConnection.TYPE_TOPIC_CONNECTION) {
              connection = new ActiveMQConnection(this, username, password, type, clientID, dupsOKBatchSize, transactionBatchSize, cacheDestinations, enable1xPrefixes, factory);
           }
        }
  
        if (connection == null) {
           throw new JMSException("Failed to create connection: invalid type " + type);
        }
        connection.setReference(this);
  
        try {
           connection.authorize(!isEnableSharedClientID());
        } catch (JMSException e) {
           try {
              connection.close();
           } catch (JMSException me) {
           }
           throw e;
        }
  
        return connection;
     }
  
     @Override
     public String toString() {
        return "ActiveMQConnectionFactory [serverLocator=" + serverLocator +
           ", clientID=" +
           clientID +
           ", consumerWindowSize=" +
           getConsumerWindowSize() +
           ", dupsOKBatchSize=" +
           dupsOKBatchSize +
           ", transactionBatchSize=" +
           transactionBatchSize +
           ", readOnly=" +
           readOnly +
           ", EnableSharedClientID=" +
           enableSharedClientID +
           "]";
     }
  
  
     private void checkWrite() {
        if (readOnly) {
           throw new IllegalStateException("Cannot set attribute on ActiveMQConnectionFactory after it has been used");
        }
     }
  
     // this may need to be set by classes which extend this class
     protected void makeReadOnly() {
        this.readOnly = true;
     }
  }
  