/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2000-2010 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package com.sun.messaging.jms.ra;

import javax.resource.*;
import javax.resource.spi.*;

import java.util.logging.Logger;

import com.sun.messaging.jmq.DestinationName;

/**
 *  Encapsulates the configuration of a MessageEndpoint.
 *  An Application Server configures an instance of this class and uses it
 *  to activate message inflow to a message listener in the application server.
 */

public class ActivationSpec
implements javax.resource.spi.ActivationSpec,
           javax.resource.spi.ResourceAdapterAssociation,
           java.io.Serializable,
           GenericConnectionFactoryProperties
{

    /** String constants used to map standard values to JMS equivalents */
    private static transient final String AUTOACKNOWLEDGE = "Auto-acknowledge";
    private static transient final String DUPSOKACKNOWLEDGE = "Dups-ok-acknowledge";
    private static transient final String NOACKNOWLEDGE = "No-acknowledge";
    private static transient final String DURABLE = "Durable";
    private static transient final String NONDURABLE = "NonDurable";
    private static transient final String QUEUE = "javax.jms.Queue";
    private static transient final String TOPIC = "javax.jms.Topic";

    /* Loggers */
    private static transient final String _className =
            "com.sun.messaging.jms.ra.ActivationSpec";
    protected static transient final String _lgrNameInboundMessage =
            "javax.resourceadapter.mqjmsra.inbound.message";
    protected static transient final Logger _loggerIM =
            Logger.getLogger(_lgrNameInboundMessage);
    protected static transient final String _lgrMIDPrefix = "MQJMSRA_AS";
    protected static transient final String _lgrMID_EET = _lgrMIDPrefix + "1001: ";
    protected static transient final String _lgrMID_INF = _lgrMIDPrefix + "1101: ";
    protected static transient final String _lgrMID_WRN = _lgrMIDPrefix + "2001: ";
    protected static transient final String _lgrMID_ERR = _lgrMIDPrefix + "3001: ";
    protected static transient final String _lgrMID_EXC = _lgrMIDPrefix + "4001: ";

    /** The resource adapter instance that this instance is bound to */
    private com.sun.messaging.jms.ra.ResourceAdapter ra = null;

    /* ActivationSpec attributes recommended for JMS RAs */
    /** The type of the destination for the MessageEndpoint consumer */
    private String destinationType = TOPIC;

    /** The name of the destination for the MessageEndpoint consumer */
    private String destination = null;

    /** The message selector for the MessageEndpoint consumer */
    private String messageSelector = null;

    /** The acknowledgement mode for the MessageEndpoitn consumer */
    private String acknowledgeMode = AUTOACKNOWLEDGE;

    /** The subscription durability of the MessageEndpoint consumer */
    private String subscriptionDurability = NONDURABLE;

    /** The clientId of the MessageEndpoint consumer */
    private String clientId = null;

    /** The subscription name of the MessageEndpoint consumer */
    private String subscriptionName = null;


    /* ActivationSpec attributes for the GlassFish(tm) MQ JMS Resource Adapter */

    /** The ConnectionFactory JNDI Name to be used */
    private String connectionFactoryJNDIName;

    /** The Maximum endpoint pool size that will be used */
    private int endpointPoolMaxSize = 15;

    /** The Steady endpoint pool size that will be used */
    private int endpointPoolSteadySize = 10;

    /** The endpoint pool re-size count that will be used */
    private int endpointPoolResizeCount = 1;

    /** The endpoint pool re-size timeout in seconds that will be used */
    private int endpointPoolResizeTimeout = 5;

    /** The Maximum # of endpoint delivery attempts for endpoints that throw an Exception */
    private int endpointExceptionRedeliveryAttempts = 6;

    /** The interval for endpoint delivery attempts for endpoints that throw an Exception */
    private int endpointExceptionRedeliveryInterval = 500;

    /** The flag indicating whether to send undeliverable messages to the DMQ */
    private boolean sendUndeliverableMsgsToDMQ = true;

    /** Indicates whether the endpoint deployment requires a shared client Identifier */
    //private boolean enableSharedClientID = false;

    /** The Message Service Address List to use to connect to GlassFish(tm) MQ */
    private String addressList = null;
    private boolean addressListSet = false;
    
    private String addressListBehavior=null;

    /** The custom acknowledgement mode for this MessageEndpoint consumer */
    private String customAcknowledgeMode = null;


    /** The username that will be used for this endpoint - default is to use the one from the RA */
    private String userName = null;

    /** The password that will be used for this endpoint - default is to use the one from the RA */
    private String password = null;
    
    private int addressListIterations;
    private boolean addressListIterationsSet=false;
    private int reconnectAttempts;
    private boolean reconnectAttemptsSet=false;
    private int reconnectInterval;
    private boolean reconnectIntervalSet=false;
    
    /** ContextClassLoader for the onMessage Thread */
    private transient ClassLoader contextClassLoader = null;
        
    /**
     * The name of the MDB (or a name which represents it). 
     * This is used when sharing non-durable topic subscriptions in a clustered container to
     * set the clientID. This then allows the subscription to be shared with the MDB of the same name
     * in the other container instances. It must be unique within an individual container instance.
     * Only used in a clustered container when shared subscriptions are enabled.
     * Only used for non-durable topic subscriptions.
     * Only used if clientID is not explicitly set, and is mandatory if this is the case.
     */
    private String mdbName = null;
   
    /**
     * If this is a clustered container, this is a name which identifies the cluster, and is always optional.
     * When sharing non-durable topic subscriptions in a clustered container this is appended to the clientID.
     * Only used in a clustered container when shared subscriptions are enabled.
     * Only used for non-durable topic subscriptions.
     * Only used if clientID is not explicitly set, and is always optional.
     */
    private String groupName = null;

    /** The Resource Adapter UID to use to ensure that ClientIDs are unique within an RA */
    private String raUID = null;

    /** The flag indicating whether this is being activated in a Clustered Container */
    private boolean inClusteredContainer = false;
    
    /**
     * The property useSharedSubscriptionInClusteredContainer is only used 
     * if this is a clustered container (as determined by inClusteredContainer), 
     * If the property useSharedSubscriptionInClusteredContainer is set  
     * then all subscriptions using this activation will be shared
     * otherwise subscriptions will not be shared
     */
    private boolean useSharedSubscriptionInClusteredContainer = true;

    /** Internal flag indicating whether this is being activated using serial msg delivery */
    /** Default (false) implies concurrent msg delivery */
    private boolean deliverySerial = false;
    
    /** additional connection factory properties, as a comma-separated list of name=value pairs */
    private String options;

    /** ActivationSpec must provide a default constructor */
    public ActivationSpec ()
    {
        _loggerIM.entering(_className, "constructor()");
    }


    // ActivationSpec interface defined methods //

    /** Validates the configuration of this ActivationSpec instance
     *  ActivationSpec instance.
     *
     *  @throws InvalidPropertyException If this activation spec
     *          instance has any invalid property
     */
    public void
    validate()
    throws InvalidPropertyException
    {
        InvalidPropertyException ipe;
        _loggerIM.entering(_className, "validate()", this);
        //Require destinationName to be valid
        if (!DestinationName.isSyntaxValid(destination)) {
            ipe = new InvalidPropertyException(_lgrMID_EXC+"validate:Invalid destination name=" + destination);
            _loggerIM.throwing(_className, "validate()", ipe);
            throw ipe;
        }

        //Require both subscriptionName and clientId to be
        //valid for durable subscriptions
        if (destinationType.equals(TOPIC) &&
            subscriptionDurability.equals(DURABLE)) {
            if ((subscriptionName == null) ||
                ("".equals(subscriptionName)) ||
                (clientId == null) ||
                ("".equals(clientId))) {
                    ipe = new InvalidPropertyException(_lgrMID_EXC+"validate:Both subscriptionName and clientId must be non-null"+
                        "\n\tsubscriptionName="+subscriptionName+"\n\tclientId="+clientId);
                    _loggerIM.throwing(_className, "validate()", ipe);
                    throw ipe;
            }
        }
        //Require valid values of endpoint properties
        if (endpointExceptionRedeliveryInterval < 1) {
            ipe = new InvalidPropertyException(_lgrMID_EXC+"validate:"+
                    "\nendpointExceptionRedeliveryInterval must be greater than 0"+
                    "\nInvalid value="+endpointExceptionRedeliveryInterval);
            _loggerIM.throwing(_className, "validate()", ipe);
            throw ipe;
        }
        if (endpointExceptionRedeliveryAttempts < 0) {
            ipe = new InvalidPropertyException(_lgrMID_EXC+"validate:"+
                    "\nendpointExceptionRedeliveryAttempts must be greater than or equal to 0"+
                    "\nInvalid value="+endpointExceptionRedeliveryAttempts);
            _loggerIM.throwing(_className, "validate()", ipe);
            throw ipe;
        }
        if (endpointPoolResizeTimeout < 1) {
            ipe = new InvalidPropertyException(_lgrMID_EXC+"validate:"+
                    "\nendpointPoolResizeTimeout must be greater than 0"+
                    "\nInvalid value="+endpointPoolResizeTimeout);
            _loggerIM.throwing(_className, "validate()", ipe);
            throw ipe;
        }
        if (endpointPoolResizeCount < 1) {
            ipe = new InvalidPropertyException(_lgrMID_EXC+"validate:"+
                    "\nendpointPoolResizeCount must be greater than 0"+
                    "\nInvalid value="+endpointPoolResizeCount);
            _loggerIM.throwing(_className, "validate()", ipe);
            throw ipe;
        }
        if (endpointPoolMaxSize < 1) {
            ipe = new InvalidPropertyException(_lgrMID_EXC+"validate:"+
                    "\nendpointPoolMaxSize must be greater than 0"+
                    "\nInvalid value="+endpointPoolMaxSize);
            _loggerIM.throwing(_className, "validate()", ipe);
            throw ipe;
        }
        if (endpointPoolSteadySize < 0) {
            ipe = new InvalidPropertyException(_lgrMID_EXC+"validate:"+
                    "\nendpointPoolSteadySize must be greater than or equal to 0"+
                    "\nInvalid value="+endpointPoolSteadySize);
            _loggerIM.throwing(_className, "validate()", ipe);
            throw ipe;
        }
        if (endpointPoolSteadySize > endpointPoolMaxSize) {
            ipe = new InvalidPropertyException(_lgrMID_EXC+"validate:"+
                    "\nendpointPoolSteadySize must be less than or equal to endpointPoolMaxSize"+
                    "\nendpointPoolSteadySize value="+endpointPoolSteadySize+
                    "\nendpointPoolMaxSize value="+endpointPoolSteadySize);
            _loggerIM.throwing(_className, "validate()", ipe);
            throw ipe;
        }
        _loggerIM.exiting(_className, "validate()");
    }

    // ResourceAdapterAssociation interface defined methods //

    /** Sets the Resource Adapter Javabean that is associated with this
     *  ActivationSpec instance.
     *
     *  @param ra The ResourceAdapter Javabean
     */
    public void
    setResourceAdapter(javax.resource.spi.ResourceAdapter ra)
    throws ResourceException
    {
        _loggerIM.entering(_className, "setResourceAdapter()", ra);
        synchronized (this) {
            if (this.ra == null) {
                if (!(ra instanceof com.sun.messaging.jms.ra.ResourceAdapter)) {
                    ResourceException rae = new ResourceException(_lgrMID_EXC+"setResourceAdapter:Incompatible ResourceAdapter class="
                        + ra.getClass());
                    _loggerIM.warning(rae.getMessage());
                    _loggerIM.throwing(_className, "setResourceAdapter()", rae);
                    throw rae;
                }
                this.ra = (com.sun.messaging.jms.ra.ResourceAdapter)ra;
                this.groupName = this.ra.getGroupName();
                this.inClusteredContainer = this.ra.getInClusteredContainer();
                this.raUID = this.ra._getRAUID();
            } else {
                ResourceException rae = new ResourceException(_lgrMID_EXC+"setResourceAdapter:Illegal to re-associate ResourceAdapter");
                _loggerIM.warning(rae.getMessage());
                _loggerIM.throwing(_className, "setResourceAdapter()", rae);
                throw rae;
            }
        }
        _loggerIM.exiting(_className, "setResourceAdapter()");
    }

    /** Gets the Resource Adapter Javabean that is associated with this
     *  ActivationSpec instance.
     *
     *  @return The ResourceAdapter Javabean
     */
    public javax.resource.spi.ResourceAdapter
    getResourceAdapter()
    {
        _loggerIM.entering(_className, "getResourceAdapter()");
        return ra;
    }


    // ActivationSpec Javabean configuration methods //
    // These Methods can throw java.lang.RuntimeException or subclasses //

    /** Sets the type of the destination for the MessageEndpoint consumer
     *
     *  @param destinationType The destination type
     *         valid values are "javax.jms.Queue" and "javax.jms.Topic"
     *
     *  @throws IllegalArgumentException If destinationType is not one of the above
     */
    public void
    setDestinationType(String destinationType)
    {
        _loggerIM.entering(_className, "setDestinationType()", destinationType);
        //Must be javax.jms.Queue or Topic
        if (QUEUE.equals(destinationType) ||
            TOPIC.equals(destinationType)) {

            this.destinationType = destinationType;

        } else {
            IllegalArgumentException iae = new IllegalArgumentException(_lgrMID_EXC+"setDestinationType:Invalid destinationType="+destinationType);
            _loggerIM.warning(iae.getMessage());
            _loggerIM.throwing(_className, "setDestinationType()", iae);
            throw iae;
        }
    }

    /** Gets the type of the destination for the MessageEndpoint consumer
     *
     *  @return The destination type
     *          values are "javax.jms.Queue" or "javax.jms.Topic"
     */
    public String
    getDestinationType()
    {
        return destinationType;
    }

    /** Sets the name of the destination for the MessageEndpoint consumer
     *
     *  @param destination The destination name
     *
     *  @throws IllegalArgumentException If destination is not a valid name
     */
    public void
    setDestination(String destination)
    {
        _loggerIM.entering(_className, "setDestination()", destination);
        //Destination must be valid name according to MQ rule
        if (!DestinationName.isSyntaxValid(destination)) {
            IllegalArgumentException iae = new IllegalArgumentException(_lgrMID_EXC+"setDestination:Invalid destination name="+destination);
            _loggerIM.warning(iae.getMessage());
            _loggerIM.throwing(_className, "setDestination()", iae);
            throw iae;
        }
        this.destination = destination;
    }

    /** Gets the name of the destination for the MessageEndpoint consumer
     *
     *  @return The destination name
     */
    public String
    getDestination()
    {
        _loggerIM.entering(_className, "getDestination()", destination);
        return destination;
    }

    /** Sets the message selector for the MessageEndpoint consumer
     *
     *  @param messageSelector The selector
     */
    public void
    setMessageSelector(String messageSelector)
    {
        _loggerIM.entering(_className, "setMessageSelector()", messageSelector);
        this.messageSelector = messageSelector;
    }

    /** Gets the message selector for the MessageEndpoint consumer
     *
     *  @return The message selector
     */
    public String
    getMessageSelector()
    {
        _loggerIM.entering(_className, "getMessageSelector()", messageSelector);
        return messageSelector;
    }

    /** Sets the acknowledgement mode for the MessageEndpoint consumer
     *
     *  @param acknowledgeMode The acknowledgement mode
     *         valid values are "Auto-acknowledge" and "Dups-ok-acknowledge"
     *         and "No-acknowledge" (Non-Durable/Topic only)
     *
     *  @throws IllegalArgumentException If acknowledgement mode is not valid
     */
    public void
    setAcknowledgeMode(String acknowledgeMode)
    {
        _loggerIM.entering(_className, "setAcknowledgeMode()", acknowledgeMode);
        //Must be Auto-acknowledge or Dups-ok-acknowledge
        if (AUTOACKNOWLEDGE.equals(acknowledgeMode) ||
            DUPSOKACKNOWLEDGE.equals(acknowledgeMode)) {

            this.acknowledgeMode = acknowledgeMode;

        } else {
            IllegalArgumentException iae = new IllegalArgumentException(_lgrMID_EXC+"setAcknowledgeMode:Invalid acknowledgeMode="+acknowledgeMode);
            _loggerIM.warning(iae.getMessage());
            _loggerIM.throwing(_className, "setAcknowledgeMode()", iae);
            throw iae;
        }
    }

    /** Gets the acknowledgement mode for the MessageEndpoint consumer
     *
     *  @return The acknowledgement mode
     *          one of either "Auto-acknowledge" or "Dups-ok-acknowledge" or null
     */
    public String
    getAcknowledgeMode()
    {
        _loggerIM.entering(_className, "getAcknowledgeMode()", acknowledgeMode);
        return acknowledgeMode;
    }

    /** Sets a custom acknowledgement mode for the MessageEndpoint consumer
     *
     *  @param customAcknowledgeMode The customAcknowledgement mode
     *         valid values are "No-acknowledge" (Non-Durable/Topic only)
     *
     *  @throws IllegalArgumentException If customAcknowledgement mode is not valid
     */
    public void
    setCustomAcknowledgeMode(String customAcknowledgeMode)
    {
        _loggerIM.entering(_className, "setCustomAcknowledgeMode()", customAcknowledgeMode);
        //Must be No-acknowledge
        if (NOACKNOWLEDGE.equals(customAcknowledgeMode)) {
            this.customAcknowledgeMode = customAcknowledgeMode;
        } else {
            IllegalArgumentException iae = new IllegalArgumentException(_lgrMID_EXC+"setCustomAcknowledgeMode:Invalid customAcknowledgeMode="+customAcknowledgeMode);
            _loggerIM.warning(iae.getMessage());
            _loggerIM.throwing(_className, "setCustomAcknowledgeMode()", iae);
            throw iae;
        }
    }

    /** Gets the CustomAcknowledgement mode for the MessageEndpoint consumer
     *
     *  @return The CustomAcknowledgement mode
     *          currently only "No-acknowledge" or null
     */
    public String
    getCustomAcknowledgeMode()
    {
        _loggerIM.entering(_className, "getCustomAcknowledgeMode()", customAcknowledgeMode);
        return customAcknowledgeMode;
    }

    /** Sets the subscription durability for the MessageEndpoint consumer
     *
     *  @param subscriptionDurability The durability mode
     *         valid values are "Durable" and "NonDurable"
     *
     *  @throws IllegalArgumentException If subscriptionDurability is not valid
     */
    public void
    setSubscriptionDurability(String subscriptionDurability)
    {
        _loggerIM.entering(_className, "setSubscriptionDurability()", subscriptionDurability);
        //Must be Durable or NonDurable
        if (DURABLE.equals(subscriptionDurability) ||
            NONDURABLE.equals(subscriptionDurability)) {

            this.subscriptionDurability = subscriptionDurability;

        } else {
            IllegalArgumentException iae = new IllegalArgumentException(_lgrMID_EXC+"setSubscriptionDurability:Invalid subscriptionDurability="+subscriptionDurability);
            _loggerIM.warning(iae.getMessage());
            _loggerIM.throwing(_className, "setSubscriptionDurability()", iae);
            throw iae;
        }
    }

    /** Gets the subscription durability for the MessageEndpoint consumer
     *
     *  @return The subscription durability
     *          one of either "Durable" or "NonDurable" or null
     */
    public String
    getSubscriptionDurability()
    {
        _loggerIM.entering(_className, "getSubscriptionDurability()", subscriptionDurability);
        return subscriptionDurability;
    }

    /** Sets the client identifier for the MessageEndpoint consumer
     *
     *  @param clientId The client identifier
     */
    public void
    setClientId(String clientId)
    {
        _loggerIM.entering(_className, "setClientId()", clientId);
        this.clientId = clientId;
    }

    /** Return the client identifier for the MessageEndpoint consumer
     *
     *  @return The client identifier
     */
    public String
    getClientId()
    {
        _loggerIM.entering(_className, "getClientId()", clientId);
        return clientId;
    }

    /** Sets the subscription name for the MessageEndpoint consumer
     *
     *  @param subscriptionName The name of the subscription
     */
    public void
    setSubscriptionName(String subscriptionName)
    {
        _loggerIM.entering(_className, "setSubscriptionName()", subscriptionName);
        this.subscriptionName = subscriptionName; 
    }

    /** Returns the subscription name for the MessageEndpoint consumer
     *
     *  @return The name of the subscription
     */
    public String
    getSubscriptionName()
    {
        _loggerIM.entering(_className, "getSubscriptionName()", subscriptionName);
        return subscriptionName;
    }

    /** Sets the connectionFactoryJNDIName for the MessageEndpoint consumer
     *
     *  @param connectionFactoryJNDIName The connectionFactoryJNDIName
     */
    public void
    _setConnectionFactoryJNDIName(String connectionFactoryJNDIName)
    {
        _loggerIM.entering(_className, "_setConnectionFactoryJNDIName()", connectionFactoryJNDIName);
        this.connectionFactoryJNDIName = connectionFactoryJNDIName; 
    }

    /** Returns the connectionFactoryJNDIName for the MessageEndpoint consumer
     *
     *  @return The connectionFactoryJNDIName
     */
    public String
    _getConnectionFactoryJNDIName()
    {
        _loggerIM.entering(_className, "_getConnectionFactoryJNDIName()", connectionFactoryJNDIName);
        return connectionFactoryJNDIName;
    }

    /** Sets the endpointPoolMaxSize for the MessageEndpoint consumer
     *
     *  @param endpointPoolMaxSize The endpointPoolMaxSize
     */
    public void
    setEndpointPoolMaxSize(int endpointPoolMaxSize)
    {
        _loggerIM.entering(_className, "setEndpointPoolMaxSize()", Integer.toString(endpointPoolMaxSize));
        if (endpointPoolMaxSize < 1) {
            IllegalArgumentException iae = new IllegalArgumentException(_lgrMID_EXC+"setEndpointPoolMaxSize:"+
                    "Value must be greater than 0"+
                    "Invalid value="+endpointPoolMaxSize);
            _loggerIM.warning(iae.getMessage());
            _loggerIM.throwing(_className, "setEndpointPoolMaxSize()", iae);
            throw iae;
        }
        this.endpointPoolMaxSize = endpointPoolMaxSize; 
    }

    /** Returns the endpointPoolMaxSize for the MessageEndpoint consumer
     *
     *  @return The endpointPoolMaxSize
     */
    public int
    getEndpointPoolMaxSize()
    {
        _loggerIM.entering(_className, "getEndpointPoolMaxSize()", Integer.toString(endpointPoolMaxSize));
        return endpointPoolMaxSize;
    }

    /** Sets the endpointPoolSteadySize for the MessageEndpoint consumer
     *
     *  @param endpointPoolSteadySize The endpointPoolSteadySize
     */
    public void
    setEndpointPoolSteadySize(int endpointPoolSteadySize)
    {
        _loggerIM.entering(_className, "setEndpointPoolSteadySize()", Integer.toString(endpointPoolSteadySize));
        if (endpointPoolSteadySize < 0) {
            IllegalArgumentException iae = new IllegalArgumentException(_lgrMID_EXC+"setEndpointPoolSteadySize:"+
                    "Value must be greater than or equal to 0"+
                    "Invalid value="+endpointPoolSteadySize);
            _loggerIM.warning(iae.getMessage());
            _loggerIM.throwing(_className, "setEndpointPoolSteadySize()", iae);
            throw iae;
        }
        this.endpointPoolSteadySize = endpointPoolSteadySize; 
    }

    /** Returns the endpointPoolSteadySize for the MessageEndpoint consumer
     *
     *  @return The endpointPoolSteadySize
     */
    public int
    getEndpointPoolSteadySize()
    {
        _loggerIM.entering(_className, "getEndpointPoolSteadySize()", Integer.toString(endpointPoolSteadySize));
        return endpointPoolSteadySize;
    }

    /** Sets the endpointPoolResizeCount for the MessageEndpoint consumer
     *
     *  @param endpointPoolResizeCount The endpointPoolResizeCount
     */
    public void
    setEndpointPoolResizeCount(int endpointPoolResizeCount)
    {
        _loggerIM.entering(_className, "setEndpointPoolResizeCount()", Integer.toString(endpointPoolResizeCount));
        if (endpointPoolResizeCount < 1) {
            IllegalArgumentException iae = new IllegalArgumentException(_lgrMID_EXC+"setEndpointPoolResizeCount:"+
                    "Value must be greater than 0:"+
                    "Invalid value="+endpointPoolResizeCount);
            _loggerIM.warning(iae.getMessage());
            _loggerIM.throwing(_className, "setEndpointPoolResizeCount()", iae);
            throw iae;
        }
        this.endpointPoolResizeCount = endpointPoolResizeCount; 
    }

    /** Returns the endpointPoolResizeCount for the MessageEndpoint consumer
     *
     *  @return The endpointPoolResizeCount
     */
    public int
    getEndpointPoolResizeCount()
    {
        _loggerIM.entering(_className, "getEndpointPoolResizeCount()", Integer.toString(endpointPoolResizeCount));
        return endpointPoolResizeCount;
    }

    /** Sets the endpointPoolResizeTimeout for the MessageEndpoint consumer
     *
     *  @param endpointPoolResizeTimeout The endpointPoolResizeTimeout
     */
    public void
    setEndpointPoolResizeTimeout(int endpointPoolResizeTimeout)
    {
        _loggerIM.entering(_className, "setEndpointPoolResizeTimeout()", Integer.toString(endpointPoolResizeTimeout));
        if (endpointPoolResizeTimeout < 1) {
            IllegalArgumentException iae = new IllegalArgumentException(_lgrMID_EXC+"setEndpointPoolResizeTimeout:"+
                    "Value must be greater than 0:"+
                    "Invalid value="+endpointPoolResizeTimeout);
            _loggerIM.warning(iae.getMessage());
            _loggerIM.throwing(_className, "setEndpointPoolResizeTimeout()", iae);
            throw iae;
        }
        this.endpointPoolResizeTimeout = endpointPoolResizeTimeout; 
    }

    /** Returns the endpointPoolResizeTimeout for the MessageEndpoint consumer
     *
     *  @return The endpointPoolResizeTimeout
     */
    public int
    getEndpointPoolResizeTimeout()
    {
        _loggerIM.entering(_className, "getEndpointPoolResizeTimeout()", Integer.toString(endpointPoolResizeTimeout));
        return endpointPoolResizeTimeout;
    }

    /** Sets the maximum number of Redelivery attempts
     *  to an Endpoint that throws an Exception.
     *  This enables the RA to stop
     *  endlessly delivering messages to an Endpoint
     *  that repeatedly throws an Exception
     *
     *  @param endpointExceptionRedeliveryAttempts The maximum number of Redelivery attempts
     */
    public void
    setEndpointExceptionRedeliveryAttempts(int endpointExceptionRedeliveryAttempts)
    {
        _loggerIM.entering(_className, "setEndpointExceptionRedeliveryAttempts()", Integer.toString(endpointExceptionRedeliveryAttempts));
        if (endpointExceptionRedeliveryAttempts < 0) {
            IllegalArgumentException iae = new IllegalArgumentException(_lgrMID_EXC+"setEndpointExceptionRedeliveryAttempts:"+
                    "Value must be greater than or equal to 0:"+
                    "Invalid value="+endpointExceptionRedeliveryAttempts);
            _loggerIM.warning(iae.getMessage());
            _loggerIM.throwing(_className, "setEndpointExceptionRedeliveryAttempts()", iae);
            throw iae;
        }
        this.endpointExceptionRedeliveryAttempts = endpointExceptionRedeliveryAttempts; 
    }

    /** Returns the the maximum number of Redelivery attempts
     *  to an Endpoint that throws an Exception.
     *  This enables the RA to stop
     *  endlessly delivering messages to an Endpoint
     *  that repeatedly throws an Exception
     *
     *  @return The maximum number of Redelivery attempts
     *          to an Endpoint.
     */
    public int
    getEndpointExceptionRedeliveryAttempts()
    {
        _loggerIM.entering(_className, "getEndpointExceptionRedeliveryAttempts()", Integer.toString(endpointExceptionRedeliveryAttempts));
        return endpointExceptionRedeliveryAttempts;
    }

    /** Sets the interval for Redelivery attempts
     *  to an Endpoint that throws an Exception.
     *
     *  @param endpointExceptionRedeliveryInterval The maximum number of Redelivery attempts
     */
    public void
    setEndpointExceptionRedeliveryInterval(int endpointExceptionRedeliveryInterval)
    {
        _loggerIM.entering(_className, "setEndpointExceptionRedeliveryInterval()", Integer.toString(endpointExceptionRedeliveryInterval));
        if (endpointExceptionRedeliveryInterval < 1) {
            IllegalArgumentException iae = new IllegalArgumentException(_lgrMID_EXC+"setEndpointExceptionRedeliveryInterval:"+
                    "Value must be greater than 0:"+
                    "Invalid value="+endpointExceptionRedeliveryInterval);
            _loggerIM.warning(iae.getMessage());
            _loggerIM.throwing(_className, "setEndpointExceptionRedeliveryInterval()", iae);
            throw iae;
        }
        this.endpointExceptionRedeliveryInterval = endpointExceptionRedeliveryInterval; 
    }

    /** Returns the interval for Redelivery attempts
     *  to an Endpoint that throws an Exception.
     *
     *  @return The interval for Redelivery attempts
     *          to an Endpoint.
     */
    public int
    getEndpointExceptionRedeliveryInterval()
    {
        _loggerIM.entering(_className, "getEndpointExceptionRedeliveryInterval()", Integer.toString(endpointExceptionRedeliveryInterval));
        return endpointExceptionRedeliveryInterval;
    }

    /** Sets whether to send undeliverable messages to the DMQ
     *
     *  @param sendUndeliverableMsgsToDMQ If true; sends the undeliverable
     *         messages to the DMQ.
     */
    public void
    setSendUndeliverableMsgsToDMQ(boolean sendUndeliverableMsgsToDMQ)
    {
        _loggerIM.entering(_className, "setSendUndeliverableMsgsToDMQ()", Boolean.toString(sendUndeliverableMsgsToDMQ));
        this.sendUndeliverableMsgsToDMQ = sendUndeliverableMsgsToDMQ; 
    }

    /** Returns whether to send undeliverable messages to the DMQ
     *
     *  @return sendUndeliverableMsgsToDM
     *  Whether to send undeliverable messages to the DMQ
     */
    public boolean
    getSendUndeliverableMsgsToDMQ()
    {
        _loggerIM.entering(_className, "getSendUndeliverableMsgsToDMQ()", Boolean.toString(sendUndeliverableMsgsToDMQ));
        return sendUndeliverableMsgsToDMQ;
    }

    /** Sets the ContextClassLoader to be used for the MessageEndpoint consumer
     *
     *  @param contextClassLoader The contextClassLoader
     */
    public void
    setContextClassLoader(ClassLoader contextClassLoader)
    {
        _loggerIM.entering(_className, "setContextClassLoader()", contextClassLoader);
        this.contextClassLoader = contextClassLoader; 
    }

    /** Returns the contextClassLoader used for the MessageEndpoint consumer
     *
     *  @return The contextClassLoader
     */
    public ClassLoader
    getContextClassLoader()
    {
        _loggerIM.entering(_className, "getContextClassLoader()", contextClassLoader);
        return contextClassLoader;
    }

	/**
	 * Sets the specified addressList for this ActivationSpec
	 * 
	 * @param addressList
	 */
	public void setAddressList(String addressList) {
		_loggerIM.entering(_className, "setAddressList()", addressList);
		this.addressList = addressList;
		if ((addressList != null) && !"".equals(addressList)) {
			this.addressListSet = true;
		}
	}

	/**
	 * Returns the specified addressList for this ActivationSpec
	 * 
	 * @return The addressList
	 */
	public String getAddressList() {
		_loggerIM.entering(_className, "getAddressList()", addressList);
		return addressList;
	}
	
	public void setAddressListBehavior(String addressListBehavior) {
		_loggerIM.entering(_className, "setAddressListBehavior()", addressListBehavior);
		this.addressListBehavior = addressListBehavior;		
	}

	public String getAddressListBehavior() {
		_loggerIM.entering(_className, "getAddressListBehavior()", addressListBehavior);
		
        if ((addressListBehavior == null) && (ra != null)) {
            return ra.getAddressListBehavior();
        } else {
            return addressListBehavior;
        }
		
	}	

	/**
	 * Set the name of the MDB (or a name which represents it). 
     * This is used when sharing non-durable topic subscriptions in a clustered container to
     * set the clientID. This then allows the subscription to be shared with the MDB of the same name
     * in the other container instances. It must be unique within an individual container instance.
     * Only used in a clustered container when shared subscriptions are enabled.
     * Only used for non-durable topic subscriptions.
     * Only used if clientID is not explicitly set, and is mandatory if this is the case.
     * 
     * @param mdbName
     */
    public void  setMdbName(String mdbName)
    {
        _loggerIM.entering(_className, "setMdbName()", mdbName);
        this.mdbName = mdbName; 
    }

	/**
	 * Returns the name of the MDB (or a name which represents it).
	 * 
	 * @return
	 */
	public String getMdbName() {
		_loggerIM.entering(_className, "getMdbName()", mdbName);
		return mdbName;
	}

	/**
	 * Sets the userName for this activationSpec.
	 * 
	 * @param userName
	 */
	public void setUserName(String userName) {
		_loggerIM.entering(_className, "setUserName()", userName);
		this.userName = userName;
	}

	/**
	 * Returns the userName configured for this activationSpec if one is set, otherwise returned the userName configured for the ResourceAdapter.
	 * 
	 * @return 
	 */
	public String getUserName() {
		_loggerIM.entering(_className, "getUserName()", userName);
		if ((userName == null) && (ra != null)) {
			return ra.getUserName();
		} else {
			return userName;
		}
	}

	/**
	 * Sets the password for this activationSpec.
	 * 
     * @param password
     */
    public void setPassword(String password) {
        _loggerIM.entering(_className, "setPassword()");
        this.password = password;
    }
 
	/**
	 * Returns the password configured for this activationSpec if one is set, otherwise returned the password configured for the ResourceAdapter.
	 * 
	 * @return 
	 */ 
    public String getPassword() {
        _loggerIM.entering(_className, "getPassword()");
        if ((password == null) && (ra != null)) {
            return ra.getPassword();
        } else {
            return password;
        }
    }
    
	/**
	 * Sets the value of addressListIterations for this activationSpec.
	 * 
     * @param password
     */
	public void setAddressListIterations(int addressListIterations) {
        _loggerIM.entering(_className, "setAddressListIterations()");
        this.addressListIterations = addressListIterations;
        this.addressListIterationsSet=true;
	}

	/**
	 * Returns the value of addressListIterations configured for this activationSpec if one is set, 
	 * otherwise returned the value of addressListIterations configured for the ResourceAdapter.
	 * 
	 * @return 
	 */
	public int getAddressListIterations() {
		if (addressListIterationsSet){
			return addressListIterations;
		} else {
			return ra.getAddressListIterations();
		}
	}

	/**
	 * Sets the value of reconnectAttempts for this activationSpec.
	 * 
     * @param password
     */
	public void setReconnectAttempts(int reconnectAttempts) {
        _loggerIM.entering(_className, "setReconnectAttempts()");
        this.reconnectAttempts = reconnectAttempts;
		this.reconnectAttemptsSet=true;
	}

	/**
	 * Returns the value of reconnectAttempts configured for this activationSpec if one is set, 
	 * otherwise returned the value of reconnectAttempts configured for the ResourceAdapter.
	 * 
	 * @return 
	 */
	public int getReconnectAttempts() {
		if (reconnectAttemptsSet){
			return reconnectAttempts;
		} else {
			return ra.getReconnectAttempts();
		}
	}
	

	/**
	 * Sets the value of reconnectInterval for this activationSpec.
	 * 
     * @param password
     */
	public void setReconnectInterval(int reconnectInterval) {
        _loggerIM.entering(_className, "setReconnectInterval()");
        this.reconnectInterval = reconnectInterval;
		this.reconnectIntervalSet=true;
	}


	/**
	 * Returns the value of reconnectInterval configured for this activationSpec if one is set, 
	 * otherwise returned the value of reconnectInterval configured for the ResourceAdapter.
	 * 
	 * @return 
	 */
	public int getReconnectInterval() {
		if (reconnectIntervalSet){
			return reconnectInterval;
		} else {
			return ra.getReconnectInterval();
		}
	}
	
	/**
	 * This is the method for setting reconnectEnabled on this activationSpec
	 * It logs a warning to say that this is not allowed
	 */
	public void setReconnectEnabled(boolean flag) {
		 _loggerIM.warning(_lgrMID_WRN+"Property reconnectEnabled cannot be set on con.sun.messaging.jms.ra.ActivationSpec, ignoring specified value");
	}

	/**
	 * Returns the value of reconnectEnabled that should be used for this activationSpec.
	 * This is always false, as the reconnection needs to be done by the resource adapter
	 * rather than the MQ client.
	 * 
	 * @return 
	 */
	public boolean getReconnectEnabled() {
		// Force reconnectEnabled to false for XAR success
		return false;
	}
    
    /**
     * Set additional arbitrary connection factory properties
     * 
     * The properties must be specified as a String containing a comma-separated list of name=value pairs
     * e.g. prop1=value1,prop2=value2
     * If a value contains a = or , you can either 
     * place the whole value between quotes (prop1="val=ue") or 
     * use \ as an escape character (prop1=val\,ue)
     * 
     * This method cannot be used to set properties which are configured internally or which have their own setter methods. These are:
     * imqReconnectEnabled, imqReconnectAttempts, imqReconnectInterval, imqDefaultUsername, 
     * imqDefaultPassword, imqAddressList, imqAddressListIterations, imqAddressListBehavior
     * 
     * Any values specified for those properties will be ignored
     *
     *
     * @param props connection factory properties as a comma-separated list of name=value pairs
     */
    public void setOptions(String props){
    	options=props;
    }
    
    public String getOptions(){
    	return options;
    }
     
    //////////////////////////////////////////////////////////////////////////////////


	/**
	 * Return the addressList to use for this activation
	 * 
	 * If addressList is specified on this ActivatioSpec then this value is returned, unmodified.
	 * If addressList is not specified on this ActivationSpec then a suitably adjusted value is obtained from the ResourceAdapter
	 * 
	 * @return
	 */
	protected String _AddressList() {
		if (addressList != null) {
			return addressList;
		} else {
			if (ra != null) {
				return ((com.sun.messaging.jms.ra.ResourceAdapter) ra)._getEffectiveConnectionURL();
			} else {
				return "localhost";
			}
		}
	}

    public void _setDeliverySerial(boolean mode) {
        deliverySerial = mode;
    }

    protected boolean _getDeliverySerial() {
        return deliverySerial;
    }

    protected boolean _deliverySerial() {
        return deliverySerial;
    }

    /**
     * If this is a clustered container, return a name which identifies the cluster
     * @return
     */
    protected String _getGroupName() {
        return groupName;
    }

    /**
     * If this is a clustered container, specify a name which identifies the cluster. This is always optional.
     * When sharing non-durable topic subscriptions in a clustered container this is appended to the clientID.
     * Only used in a clustered container when shared subscriptions are enabled.
     * Only used for non-durable topic subscriptions.
     * Only used if clientID is not explicitly set, and is always optional.
     *
     * @param groupName
     */
    protected void _setGroupName(String groupName) {
        this.groupName = groupName;
    }

    protected String _getRAUID() {
        return raUID;
    }

    protected void _setRAUID(String raUID) {
        this.raUID = raUID;
    }

    protected void _setInClusteredContainer(boolean inClusteredContainer) {
        this.inClusteredContainer = inClusteredContainer;
    }

    protected boolean _isInClusteredContainerSet() {
        return inClusteredContainer;
    }

    protected boolean _isNoAckDeliverySet() {
        return (NOACKNOWLEDGE.equals(customAcknowledgeMode));
    }

    protected boolean _isDurableSet() {
        return (DURABLE.equals(subscriptionDurability));
    }

    protected boolean _isDestTypeQueueSet() {
        return (QUEUE.equals(destinationType));
    }

    protected boolean _isDestTypeTopicSet() {
        return (TOPIC.equals(destinationType));
    }

    /**
     * Return whether this activation should use RADirect. 
     * Return true if the RA is configured to use RADirect and we haven't overridden addressList in the activation spec.
     * Otherwise return false.
     * @return
     */
    protected boolean useRADirect() {
        if (this.ra != null){
            return (ra._isRADirect() && !addressListSet);
        } else {
            return false; 
        }
    }
    
    public String
    toString()
    {
        return ("ActvationSpec configuration=\n"+
                "\tDestinationType                     ="+destinationType+"\n"+
                "\tDestination                         ="+destination+"\n"+
                "\tMessageSelector                     ="+messageSelector+"\n"+
                "\tAcknowledgeMode                     ="+acknowledgeMode+"\n"+
                "\tSubscriptionDurability              ="+subscriptionDurability+"\n"+
                "\tuseSharedSubscriptionInClusteredContainer="+useSharedSubscriptionInClusteredContainer+"\n"+
                "\tClientId                            ="+clientId+"\n"+
                "\tSubscriptionName                    ="+subscriptionName+"\n"+
                "\tEndpointPoolMaxSize                 ="+endpointPoolMaxSize+"\n"+
                "\tEndpointPoolSteadySize              ="+endpointPoolSteadySize+"\n"+
                "\tEndpointPoolResizeCount             ="+endpointPoolResizeCount+"\n"+
                "\tEndpointPoolResizeTimeout           ="+endpointPoolResizeTimeout+"\n"+
                "\tEndpointExceptionRedeliveryAttempts ="+endpointExceptionRedeliveryAttempts+"\n"+
                "\tEndpointExceptionRedeliveryInterval ="+endpointExceptionRedeliveryInterval+"\n"+
                "\tSendUndeliverableMsgsToDMQ          ="+sendUndeliverableMsgsToDMQ+"\n"+
                "\tGroupName                           ="+groupName+"\n"+
                "\tRAUID                               ="+raUID+"\n"+
                "\tInClusteredContainer                ="+inClusteredContainer+"\n"+
                "\tMdbName                             ="+mdbName+"\n"+
                "\tUserName                            ="+userName+"\n"+
                "\tAddressList (configured)            ="+addressList+"\n"+
                "\tAddressList (in effect)             ="+_AddressList()+"\n"+
                "\taddressListBehavior (configured)    ="+addressListBehavior+"\n"+
                "\taddressListBehavior (in effect)     ="+getAddressListBehavior()+"\n"+
                "\taddressListIterations (configured)  ="+addressListIterations+"\n"+
                "\taddressListIterations (in effect)   ="+getAddressListIterations()+"\n"+
                "\treconnectAttempts (configured)      ="+reconnectAttempts+"\n"+
                "\treconnectAttempts (in effect)       ="+getReconnectAttempts()+"\n"+
                "\treconnectInterval (configured)      ="+reconnectInterval+"\n"+
                "\treconnectInterval  (in effect)      ="+getReconnectInterval()+"\n"+
                "\toptions                             ="+options+"\n");
    }


	public boolean isUseSharedSubscriptionInClusteredContainer() {
		return useSharedSubscriptionInClusteredContainer;
	}


	public void setUseSharedSubscriptionInClusteredContainer(
			boolean useSharedSubscriptionInClusteredContainer) {
		this.useSharedSubscriptionInClusteredContainer = useSharedSubscriptionInClusteredContainer;
	}














}
