/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-2004 Bull S.A.
 * Contact: jonas-team@ow2.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id: MessageDrivenDesc.java 15428 2008-10-07 11:20:29Z sauthieg $
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.deployment.ejb;

import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

import org.ow2.jonas.deployment.common.DeploymentDescException;
import org.ow2.jonas.deployment.common.xml.JLinkedList;
import org.ow2.jonas.deployment.ejb.xml.ActivationConfig;
import org.ow2.jonas.deployment.ejb.xml.ActivationConfigProperty;
import org.ow2.jonas.deployment.ejb.xml.AssemblyDescriptor;
import org.ow2.jonas.deployment.ejb.xml.JonasMessageDriven;
import org.ow2.jonas.deployment.ejb.xml.MessageDriven;
import org.ow2.jonas.deployment.ejb.xml.MessageDrivenDestination;
import org.ow2.jonas.lib.util.BeanNaming;





import org.objectweb.util.monolog.api.BasicLevel;

/**
 * Class to hold meta-information related to a message driven bean
 *
 * @author Christophe Ney [cney@batisseurs.com] : Initial developer
 * @author Helene Joanin
 */
public class MessageDrivenDesc extends BeanDesc {

    public final static int AUTO_ACKNOWLEDGE = 1;
    public final static int DUPS_OK_ACKNOWLEDGE = 2;
    protected static final String[] ACKMODE = {null, "AUTO_ACKNOWLEDGE",
            "DUPS_OK_ACKNOWLEDGE"};

    public final static int SUBS_DURABLE = 1;
    public final static int SUBS_NON_DURABLE = 2;
    protected static final String[] SUBS_DURABILITY = {null,
            "SUBSCRIPTION_DURABLE", "SUBSCRIPTION_NON_DURABLE"};

    public final static int DEFAULT_MAX_MESSAGES = 1;

    protected String selector = null;
    protected int acknowledgeMode;
    protected int subscriptionDurability = SUBS_NON_DURABLE;
    protected Class destinationType = null;
    protected int txAttribute = MethodDesc.TX_NOT_SET; // for onMessage method
    protected boolean isTopicDestination = false;
    protected int transactionType;
    protected String destinationJndiName = null;
    protected String destinationLink = null;
    
    // Used with JMS Rars
    protected String destination = null;

    protected ActivationConfigDesc mdActivationConfigDesc = null;

    protected ActivationConfigDesc mdJonasActivationConfigDesc = null;

    /**
     * package protected constructor used by API
     */
    MessageDrivenDesc(ClassLoader classLoader, MessageDriven md,
            AssemblyDescriptor asd, JonasMessageDriven jMd,
            JLinkedList jMDRList, String fileName)
            throws DeploymentDescException {

        super(classLoader, md, jMd, asd, jMDRList, fileName);
        mdActivationConfigDesc = new ActivationConfigDesc(md.getActivationConfig());
        mdJonasActivationConfigDesc = new ActivationConfigDesc(jMd.getActivationConfig());

        // min-pool-size
        if (jMd.getMinPoolSize() != null) {
            String tstr = jMd.getMinPoolSize();
            Integer tval = new Integer(tstr);
            poolMin = tval.intValue();
        }

        // max-cache-size
        if (jMd.getMaxCacheSize() != null) {
            String tstr = jMd.getMaxCacheSize();
            Integer tval = new Integer(tstr);
            cacheMax = tval.intValue();
        }

        // necessary check
        if (md.getEjbName() == null) {
            throw new Error("No ejb-name specified for a message-driven bean");
        }

        // transaction-type
        if (md.getTransactionType().equals("Bean")) {
            transactionType = BEAN_TRANSACTION_TYPE;
        } else if (md.getTransactionType().equals("Container")) {
            transactionType = CONTAINER_TRANSACTION_TYPE;
        } else {
            throw new DeploymentDescException(
                    "Invalid transaction-type content for ejb-name " + ejbName);
        }

        // message driven destination
        if (jMd.getJonasMessageDrivenDestination() != null) {
            destinationJndiName = jMd.getJonasMessageDrivenDestination()
                    .getJndiName();
        }

        // Set values can be from old 2.0 way, ActivationConfig in std xml, 
        //  or ActivationConfig in jonas xml
        
        // message selector
        selector = md.getMessageSelector();
        // acknowledge mode
        if (md.getAcknowledgeMode() == null) {
            acknowledgeMode = AUTO_ACKNOWLEDGE;
        } else {
            if (md.getAcknowledgeMode().equals("Auto-acknowledge")) {
                acknowledgeMode = AUTO_ACKNOWLEDGE;
            } else  if (md.getAcknowledgeMode().equals("Dups-ok-acknowledge")) {
                acknowledgeMode = DUPS_OK_ACKNOWLEDGE;
            } else {
                throw new DeploymentDescException("Invalid acknowledge-mode content for ejb-name " + ejbName);
            }
        }
        MessageDrivenDestination d = md.getMessageDrivenDestination();
        if (d != null && d.getDestinationType() != null) {
            if (d.getDestinationType().equals("javax.jms.Queue")) {
                destinationType = javax.jms.Queue.class;
            } else  if (d.getDestinationType().equals("javax.jms.Topic")) {
                destinationType = javax.jms.Topic.class;
                isTopicDestination = true;
            } else {
                try {
                    destinationType = classLoader.loadClass(d.getDestinationType());
                } catch (Exception ex) {
                    throw new DeploymentDescException("Invalid destination-type for ejb-name " + ejbName);
                }
            }
            if (d.getSubscriptionDurability() != null) {
                if (destinationType.equals(javax.jms.Queue.class)) {
                    throw new DeploymentDescException("subscription-durability of message-driven-destination for ejb-name " 
                                                      + ejbName + " defined");
                }
                if (d.getSubscriptionDurability().equals("Durable")) {
                    subscriptionDurability = SUBS_DURABLE;
                } else  if (d.getSubscriptionDurability().equals("NonDurable")) {
                    subscriptionDurability = SUBS_NON_DURABLE;
                } else {
                    throw new DeploymentDescException("Invalid subscription-durability content for ejb-name " + ejbName);
                }
            } else {
                // non-durable subscription default value for topic
                if (destinationType.equals(javax.jms.Topic.class)) {
                    subscriptionDurability = SUBS_NON_DURABLE;
                }
            }

        }

        destinationLink = md.getMessageDestinationLink();

        if( mdActivationConfigDesc != null) {
            configureAC(mdActivationConfigDesc, classLoader);
        } 
        
        if( mdJonasActivationConfigDesc != null) {
            configureAC(mdJonasActivationConfigDesc, classLoader);
        } 
        
        if (destinationJndiName == null) {
            throw new Error("No destination specified for message-driven bean " + ejbName);
        }

        // cache TxAttribute for onMessage and ejbTimeout
        for (Iterator i = getMethodDescIterator(); i.hasNext();) {
            MethodDesc methd = (MethodDesc) i.next();
            if (methd.getMethod().getName().equals("onMessage")) {
                txAttribute = methd.getTxAttribute();
            }
            if (methd.getMethod().getName().equals("ejbTimeout")) {
                timerTxAttribute = methd.getTxAttribute();
                ejbTimeoutSignature = BeanNaming.getSignature(getEjbName(), methd.getMethod());
            }
        }
    }

    /**
     * Get transaction management type of the message driven.
     *
     * @return transaction type value within
     *         BEAN_TRANSACTION_TYPE,CONTAINER_TRANSACTION_TYPE
     */
    public int getTransactionType() {
        return transactionType;
    }

    /**
     * Return the transaction attribute for the onMessage method of Message
     * driven bean
     *
     */
    public int getTxAttribute() {
        return txAttribute;
    }

    /**
     * @return true if BEAN_TRANSACTION_TYPE
     */
    public boolean isBeanManagedTransaction() {
        return (transactionType == BeanDesc.BEAN_TRANSACTION_TYPE);
    }

    /**
     * Get the the destination name of the message driven bean.
     * @return name of the destination of the message driven bean.
     */
    public String getDestination() {
        return(destination);
    }

    /**
     * Get the the destination JNDI name of the message driven bean.
     *
     * @return JNDI name of the destination of the message driven bean.
     */
    public String getDestinationJndiName() {
        return (destinationJndiName);
    }

    /**
     * Get the the destination link name of the message driven bean.
     *
     * @return link name of the destination of the message driven bean.
     */
    public String getDestinationLink() {
        return (destinationLink);
    }

    /**
     * Get the the destination type of the message driven bean.
     *
     * @return type of the destination of the message driven bean.
     */
    public Class getDestinationType() {
        return (destinationType);
    }

    /**
     * Return true if it is a Topic destination
     */
    public boolean isTopicDestination() {
        return (isTopicDestination);
    }

    /**
     * Assessor for existence of a message-selector for the message driven bean
     *
     * @return true if message-selector is defined for the bean
     */
    public boolean hasSelector() {
        return (selector != null);
    }

    /**
     * Get the message-selector value of the message driven bean.
     *
     * @return value of the message selector return null if no selector
     */
    public String getSelector() {
        return (selector);
    }

    /**
     * Get the acknowledge-mode of the message driven bean.
     *
     * @return acknowledge-mode value within AUTO_ACKNOWLEDGE,
     *         DUPS_OK_ACKNOWLEDGE
     */
    public int getAcknowledgeMode() {
        return (acknowledgeMode);
    }

    /**
     * Get the the durability of the subscription of the message driven bean.
     *
     * @return durability of the subscription value within SUBS_DURABLE,
     *         SUBS_NON_DURABLE
     */
    public int getSubscriptionDurability() {
        return (subscriptionDurability);
    }

    public boolean isSubscriptionDurable() {
        return (subscriptionDurability == SUBS_DURABLE);
    }

    /**
     * Return true if tx attribute for onMessage is Required
     */
    public boolean isRequired() {
        return (txAttribute == MethodDesc.TX_REQUIRED);
    }

    /**
     * @return the maximum number of messages that can be assigned to a server
     *         session at one time. will be configurable in the future
     */
    public int getMaxMessages() {
        return DEFAULT_MAX_MESSAGES;
    }

    /**
     * check that trans-attribute is valid for bean
     */
    protected void checkTxAttribute(MethodDesc md)
            throws DeploymentDescException {
        java.lang.reflect.Method m = md.getMethod();
        if (getTransactionType() == CONTAINER_TRANSACTION_TYPE) {
            if (md.getTxAttribute() == MethodDesc.TX_NOT_SET) {
                // trans-attribute not set !
                // trace a warning and set the tx-attribute with the default value
                logger.log(BasicLevel.WARN,
                           "trans-attribute missing for method "
                           + m.toString() + " in message driven bean "
                           + getEjbName()
                           + " (set to the default value "
                           + MethodDesc.TX_STR_DEFAULT_VALUE_4_MDB
                           + ")");
                md.setTxAttribute(MethodDesc.TX_STR_DEFAULT_VALUE_4_MDB);
            }
            if ((md.getTxAttribute() != MethodDesc.TX_REQUIRED)
                    && (md.getTxAttribute() != MethodDesc.TX_NOT_SUPPORTED)) {
                if (!"ejbTimeout".equals(md.getMethod().getName())) {
                    throw new DeploymentDescException(md.getTxAttributeName()
                            + " is not a valid trans-attribute for method "
                            + m.toString() + " in message driven bean "
                            + getEjbName());
                }
            }
        } else {
            if (md.getTxAttribute() != MethodDesc.TX_NOT_SET) {
                throw new DeploymentDescException(
                        "trans-attribute for message driven bean "
                                + getEjbName()
                                + " must not be defined (transaction bean managed)");
            }
        }
    }

    /**
     * Check that the message diven bean descriptor is valid
     *
     * @exception DeploymentDescException
     *                thrown for non-valid bean
     */
    public void check() throws DeploymentDescException {
        super.check();
    }

    /**
     * String representation of the object for test purpose
     *
     * @return String representation of this object
     */
    public String toString() {
        StringBuffer ret = new StringBuffer();
        ret.append(super.toString());
        ret.append("\ngetTransactionType()=" + TRANS[getTransactionType()]);
        if (hasSelector()) {
            ret.append("\ngetSelector()=\"" + getSelector() + "\"");
        }
        ret.append("\ngetAcknowledgeMode()=" + ACKMODE[getAcknowledgeMode()]);
        if (getDestinationType() != null) {
            ret.append("\ngetDestinationType()="
                    + getDestinationType().getName());
        }
        ret.append("\ngetSubscriptionDurability()="
                + SUBS_DURABILITY[getSubscriptionDurability()]);
        ret.append("\ngetDestinationJndiName()=" + getDestinationJndiName());
        return ret.toString();
    }


    /**
     * @return the MessageDriven ActivationConfigDesc object
     */
    public ActivationConfigDesc getMdActivationConfigDesc() {
        return mdActivationConfigDesc;
    }

    /**
     * @return the JOnAS MessageDriven ActivationConfigDesc object
     */
    public ActivationConfigDesc getJonasMdActivationConfigDesc() {
        return mdJonasActivationConfigDesc;
    }
    
    private void configureAC(ActivationConfigDesc ac, ClassLoader curLoader) throws DeploymentDescException{
        try {
            List acpl = ac.getActivationConfigPropertyList();
            for (ListIterator lit = acpl.listIterator();lit.hasNext();) {
                ActivationConfigPropertyDesc el = (ActivationConfigPropertyDesc)lit.next();
                if (el.getActivationConfigPropertyName().equals("destinationType")) {
                    if (el.getActivationConfigPropertyValue().equals("javax.jms.Queue")) {
                        destinationType = javax.jms.Queue.class;
                    } else if (el.getActivationConfigPropertyValue().equals("javax.jms.Topic")) {
                        destinationType = javax.jms.Topic.class;
                        isTopicDestination = true;
                    } else {
                        try {
                            destinationType = curLoader.loadClass(el.getActivationConfigPropertyValue());
                        } catch (Exception ex) {
                            throw new DeploymentDescException("Invalid destination-type of " 
                                                  + el.getActivationConfigPropertyValue() 
                                                  + "for ejb-name" + ejbName);
                        }
                    } 
                } else if (el.getActivationConfigPropertyName().equals("messageSelector")) {
                    selector = el.getActivationConfigPropertyValue();
                } else if (el.getActivationConfigPropertyName().equals("acknowledgeMode")) {
                    if (el.getActivationConfigPropertyValue().equals("Auto-acknowledge")) {
                        acknowledgeMode = AUTO_ACKNOWLEDGE;
                    } else  if (el.getActivationConfigPropertyValue().equals("Dups-ok-acknowledge")) {
                        acknowledgeMode = DUPS_OK_ACKNOWLEDGE;
                    }
                } else if (el.getActivationConfigPropertyName().equals("subscriptionDurability")) {
                    if (el.getActivationConfigPropertyValue().equals("Durable")) {
                        subscriptionDurability = SUBS_DURABLE;
                    } else {
                        subscriptionDurability = SUBS_NON_DURABLE;
                    }
                } else if (el.getActivationConfigPropertyName().equals("destination")) {
                    destination = el.getActivationConfigPropertyValue();
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    
}

