/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-2007 Bull S.A.S.
 * 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: EjbDeploymentDescManager.java 18310 2009-08-20 12:23:22Z sauthieg $
 * --------------------------------------------------------------------------
 */


package org.ow2.jonas.deployment.ejb.lib;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.jar.JarFile;
import java.util.jar.JarEntry;

import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
import org.ow2.jonas.deployment.api.IJNDIEnvRefsGroupDesc;
import org.ow2.jonas.deployment.api.IServiceRefDesc;
import org.ow2.jonas.deployment.common.DeploymentDescException;
import org.ow2.jonas.deployment.common.EjbLocalRefDesc;
import org.ow2.jonas.deployment.common.EjbRefDesc;
import org.ow2.jonas.deployment.common.MessageDestinationRefDesc;
import org.ow2.jonas.deployment.common.digester.JDigester;
import org.ow2.jonas.deployment.common.lib.AbsDeploymentDescManager;
import org.ow2.jonas.deployment.common.xml.JonasMessageDestination;
import org.ow2.jonas.deployment.ejb.BeanDesc;
import org.ow2.jonas.deployment.ejb.DeploymentDesc;
import org.ow2.jonas.deployment.ejb.DeploymentDescEjb1_1;
import org.ow2.jonas.deployment.ejb.DeploymentDescEjb2;
import org.ow2.jonas.deployment.ejb.EjbjarDTDs;
import org.ow2.jonas.deployment.ejb.EjbjarSchemas;
import org.ow2.jonas.deployment.ejb.EntityDesc;
import org.ow2.jonas.deployment.ejb.JonasEjbjarDTDs;
import org.ow2.jonas.deployment.ejb.JonasEjbjarSchemas;
import org.ow2.jonas.deployment.ejb.MessageDrivenDesc;
import org.ow2.jonas.deployment.ejb.SessionDesc;
import org.ow2.jonas.deployment.ejb.rules.EjbJarRuleSet;
import org.ow2.jonas.deployment.ejb.rules.JonasEjbJarRuleSet;
import org.ow2.jonas.deployment.ejb.xml.EjbJar;
import org.ow2.jonas.deployment.ejb.xml.JonasEjbJar;
import org.ow2.jonas.deployment.ws.PortComponentDesc;
import org.ow2.jonas.deployment.ws.PortComponentRefDesc;
import org.ow2.jonas.deployment.ws.WSDeploymentDescException;
import org.ow2.jonas.deployment.ws.lib.WSDeploymentDescManager;
import org.ow2.jonas.lib.util.BeanNaming;
import org.ow2.jonas.lib.util.Log;
import org.ow2.util.url.URLUtils;

/**
 * This class provide a way for managing the EjbDeploymentDesc.
 * Note that there is an intance of the EjbDeploymentDescManager on each JOnAS
 * server.
 * @author Ludovic Bert
 * @author Florent Benoit
 * @author Nicolas Van Caneghem <nicolas.vancaneghem@openpricer.com>
 *         Allow the deployment of an exploded ear
 * Contributors:<br>
 * JOnAS 4.0 Adriana Danes: keep deployement descriptors is a Stringified format
 * JOnAS 5.0 S. Ali Tokmen: fixed URL escaping issues when folders contain spaces
 * for management support.
 */
public class EjbDeploymentDescManager extends AbsDeploymentDescManager {

    /**
     * ejb-jar.xml filename
     */
    public static final String EJB_JAR_FILE_NAME = "META-INF/ejb-jar.xml";

    /**
     * jonas-ejb-jar.xml filename
     */
    public static final String JONAS_EJB_JAR_FILE_NAME = "META-INF/jonas-ejb-jar.xml";

    /**
     * Flag for parser validation
     */
    private static boolean parsingWithValidation = true;

    /**
     * The unique instance of the EjbDeploymentDescManager.
     */
    private static EjbDeploymentDescManager unique;


    /**
     * Digester used to parse ejb-jar.xml
     */
    private static JDigester ejbjarDigester = null;

    /**
     * Digester use to parse jonas-ejb-ja.xml
     */
    private static JDigester jonasEjbjarDigester = null;

    /**
     * Rules to parse the application-client.xml
     */
    private static EjbJarRuleSet ejbjarRuleSet = new EjbJarRuleSet();

    /**
     * Rules to parse the jonas-client.xml
     */
    private static JonasEjbJarRuleSet jonasEjbjarRuleSet = new JonasEjbJarRuleSet();


    /**
     * Reference on the WSDeploymentDescManager.
     */
    private WSDeploymentDescManager wsDDManager = null;

    /**
     * Associates an URL of an ejb-jar to its ejb deployment descriptor.
     * It's the cache.
     */
    private Hashtable urlJarBindings;

    /**
     * Associates an URL to an ear classloader. (ear only)
     */
    private Hashtable urlEarCLBindings;

    /**
     * Associates an URL to an ejb classloader. (ear only)
     */
    private Hashtable urlEjbCLBindings;

    /**
     * Associates an URL to an alternate DDesc file.
     */
    private Hashtable urlAltDDBindings = null;

    /**
     * Associates an ear classloader to a list of URLs. It is used to know if
     * the ejb-link or message-destination-link is authorised. An ejb-link or
     * mesage-destination is authorized only in the same ear.
     */
    private Hashtable earCLEjbLinkJar;

    /**
     * Logger for the deployment desc manager.
     */
    private static Logger logger = Log.getLogger("org.ow2.jonas.deployment.ejb");

    /**
     * cache used for WsGen (multiple calls)
     */
    private static Hashtable staticCache = new Hashtable();

    /**
     * Xml content of the standard deployement descriptor file
     */
    private static String xmlContent = "";

    /**
     * Xml content of the JOnAS deployement descriptor file
     */
    private static String jonasXmlContent = "";

    /**
     * Contructs a unique new EjbDeploymentDescManager.
     */
    private EjbDeploymentDescManager() {
        urlJarBindings = new Hashtable();
        urlEarCLBindings = new Hashtable();
        urlEjbCLBindings = new Hashtable();
        earCLEjbLinkJar = new Hashtable();
        urlAltDDBindings = new Hashtable();
    }

    /**
     * Get an instance of the EjbDeploymentDescManager.
     * @return the instance of the EjbDeploymentDescManager.
     */
    public static EjbDeploymentDescManager getInstance() {
        if (unique == null) {
            unique = new EjbDeploymentDescManager();
        }
        return unique;
    }

    /**
     * Factory method using the ejb-jar file name.
     * Used by GenIC/GenIDL/WsGen.
     * @param ejbjar ejbjar file name
     * @param ejbLoader classloader used to load bean classes.
     * @return instance of the corresponding DeploymentDesc
     * @exception DeploymentDescException when DeploymentDesc cannot be created with
     * given ejb-jar file.
     */
    public static DeploymentDesc getDeploymentDesc(final String ejbjar, final ClassLoader ejbLoader)
        throws DeploymentDescException {
        if (!staticCache.containsKey(ejbjar)) {
            return getDeploymentDescriptor(ejbjar, ejbLoader, (String) null);
        } else {
            return (DeploymentDesc) staticCache.get(ejbjar);
        }
    }

    /**
     * Get the specified ejb deployment descriptor and put it in the cache
     * if it is not in.
     * Called by createContainer & WebDeploymentDescManager
     * @param url the url where to load xml deployment descriptors.
     * @param ejbLoader classloader used to load bean classes.
     * @param earLoader the parent classloader (the ear classloader). Null if
     * there in the case of an ejb-jar application.
     * @return DeploymentDesc the ejb deployment descriptor. (This method is
     * used for the ear applications).
     * @throws DeploymentDescException when DeploymentDesc cannot be created
     * with the given files.
     */
    public synchronized DeploymentDesc getDeploymentDesc(final URL url,
                                                         final ClassLoader ejbLoader,
                                                         final ClassLoader earLoader)
        throws DeploymentDescException {
        // load an instance of the WebService Manager
        if (wsDDManager == null) {
            wsDDManager = WSDeploymentDescManager.getInstance();
        }

        // Check in the ejb-link is allowed : the ejb-link must be done
        // on an ejb-jar defined in the ear application.
        if (earLoader != null) {
            checkEjbLinkAvailable(earLoader, url);
        }

        String ddPath = URLUtils.urlToFile(url).getPath();
        DeploymentDesc dd = (DeploymentDesc) urlJarBindings.get(ddPath);
        if (dd == null) {
            // dd not in cache => load the deployment descriptor.
            dd = loadDeploymentDesc(url, ejbLoader, earLoader);
        }
        return dd;
    }

    /**
     * Get the deployment descriptor of the ejb-link of the specified url.
     * @param currentUrl the URL of the component (web or bean)
     * that do an ejb-link.
     * @param urlToLoad the URL of the bean to get the deployment descriptor.
     * @return the deployment descriptor of the specified bean.
     * @throws DeploymentDescException when DeploymentDesc cannot be created
     * with the given files.
     */
    private DeploymentDesc getDeploymentDesc(final URL currentUrl, final URL urlToLoad)
        throws DeploymentDescException {
        DeploymentDesc ejbLinkDD = (DeploymentDesc) urlJarBindings.get(URLUtils.urlToFile(urlToLoad).getPath());
        if (ejbLinkDD == null) {
            // ejbLinkDD not in cache => load the deployment descriptor.
            String url = URLUtils.urlToFile(currentUrl).getPath();
            URLClassLoader earLoader = (URLClassLoader) urlEarCLBindings.get(url);

            // URL to load is a valid file ?
            if (!URLUtils.urlToFile(urlToLoad).exists()) {
                throw new DeploymentDescException("There is an ejb-link from the '" + currentUrl + "' URL to the '"
                        + urlToLoad + "' URL but the last file does not exist. Check ejb-link in the first file");
            }

            if (new File(url).isFile()) {
                if (!url.toLowerCase().endsWith(".jar")) {
                    String err = "File '" + url + "' is not a jar file";
                    throw new DeploymentDescException(err);
                }
            }

            if (earLoader != null) {
                // In the case of an ear application.

                // Check in the ejb-link is allowed : the ejb-link must be done
                // on an ejb-jar defined in the ear application.
                checkEjbLinkAvailable(earLoader, urlToLoad);

                // get the loader for classes (ejb classloader).
                URLClassLoader loaderForCls = (URLClassLoader) urlEjbCLBindings.get(url);

                // Build a default classloader
                if (loaderForCls == null) {
                    loaderForCls = new URLClassLoader(new URL[] {urlToLoad});
                }

                // get the ear classloader.
                URLClassLoader loader = (URLClassLoader) urlEarCLBindings.get(url);
                ejbLinkDD = loadDeploymentDesc(urlToLoad, loaderForCls, loader);
            } else {
                // In the case of a non ear application.
                if (!currentUrl.getFile().equals(urlToLoad)) {
                    String err = "In '" + url + "' ejb-link is not allowed outside the ejb-jar if you are not in an ear application.";
                    throw new DeploymentDescException(err);
                }
                // else nothing to do.
            }
        }
        return ejbLinkDD;
    }

    /**
     * Load the specified ejb deployment descriptor and
     * put it in cache.
     * @param url where to load xml deployment descriptors.
     * @param ejbLoader classloader used to load bean classes.
     * @param earLoader the parent classloader (the ear classloader). Null when
     * not in the case of an ear application.
     * @return DeploymentDesc the ejb deployment descriptor.
     * @throws DeploymentDescException when DeploymentDesc cannot be created
     * with the given files.
     */
    private DeploymentDesc loadDeploymentDesc(final URL url,
                                              final ClassLoader ejbLoader,
                                              final ClassLoader earLoader)
        throws DeploymentDescException {

        // Check if the ejb-jar exists.
        File f = URLUtils.urlToFile(url);
        String filename = f.getPath();
        if (!f.exists()) {
            throw new DeploymentDescException(filename + " not found");
        }

        // Get the instance of the DeploymentDesc.
        DeploymentDesc dd = null;
        if (f.isDirectory()) {
            String ejbjarXmlPath = new File(filename, EJB_JAR_FILE_NAME).getPath();
            dd = getDeploymentDescriptor(ejbjarXmlPath,
                                         BeanNaming.getJonasXmlName(ejbjarXmlPath),
                                         ejbLoader,
                                         f.getAbsolutePath());

        } else if (filename.toLowerCase().endsWith(".xml")) {
            // This is an XML file name: Treat it for upward compatibility
            // f is the ejb-jar.xml file, jonas specific is colocated to this file.
            File parent = f.getParentFile();
            dd = getDeploymentDescriptor(filename,
                                         BeanNaming.getJonasXmlName(filename),
                                         ejbLoader,
                                         parent.getAbsolutePath());
        } else {
            // This is an ejb-jar
            // Check if there is an altenate DD for this URL (ear only)
            String altname = null;
            URL altDDUrl = (URL) urlAltDDBindings.get(URLUtils.urlToFile(url).getPath());
            if (altDDUrl != null) {
                altname = URLUtils.urlToFile(altDDUrl).getPath();
            }
            dd = getDeploymentDescriptor(filename, ejbLoader, altname);
        }

        // Put it in the cache in case of ear.
        if (earLoader != null) {
            urlEjbCLBindings.put(filename, ejbLoader);
            urlEarCLBindings.put(filename, earLoader);
        }

        BeanDesc[] bd = dd.getBeanDesc();
        for (int j = 0; j < bd.length; j++) {

            // Resolve the ejb-link for ejb-ref
            EjbRefDesc[] ejbRef = bd[j].getEjbRefDesc();
            for (int i = 0; i < ejbRef.length; i++) {
                String jndiName = ejbRef[i].getJndiName();
                String ejbLink = ejbRef[i].getEjbLink();
                String ejbRefType = ejbRef[i].getEjbRefType();
                if (ejbLink != null && jndiName == null) {
                    String ejbName = getJndiName(url, ejbLink, earLoader, ejbRefType, dd, true);
                    ejbRef[i].setJndiName(ejbName);
                }
            }

            // Resolve the ejb-link for ejb-local-ref
            EjbLocalRefDesc[] ejbLocalRef = bd[j].getEjbLocalRefDesc();
            for (int i = 0; i < ejbLocalRef.length; i++) {
                String ejblink = ejbLocalRef[i].getEjbLink();
                if (ejblink == null) {
                    String err = "Ejb-link must be specified for ejb-local-ref " + ejbLocalRef[i].getEjbRefName();
                    throw new DeploymentDescException(err);
                }
                String ejbRefType = ejbLocalRef[i].getEjbRefType();
                String ejbName = getJndiName(url, ejblink, earLoader, ejbRefType, dd, false);
                ejbLocalRef[i].setJndiLocalName(ejbName);
            }

            // Resolve the port-component-link for service-ref
            IServiceRefDesc[] serviceRef = bd[j].getServiceRefDesc();

            for (int i = 0; i < serviceRef.length; i++) {
                List pcRefs = serviceRef[i].getPortComponentRefs();

                for (int k = 0; k < pcRefs.size(); k++) {
                    // for each service portComponents : resolve links
                    PortComponentRefDesc pcr = (PortComponentRefDesc) pcRefs.get(k);
                    String pclink = pcr.getPortComponentLink();
                    if (pclink != null) {
                        // a pc link is defined, we resolve it
                        PortComponentDesc pcDesc = getPCDesc(url, pclink, ejbLoader, earLoader);
                        pcr.setPortComponentDesc(pcDesc);
                    }
                }
            }

            // Resolve the message-destination-link for message-destination-ref
            MessageDestinationRefDesc[] mdRef = bd[j].getMessageDestinationRefDesc();
            for (int i = 0; i < mdRef.length; i++) {
                String jndiName = mdRef[i].getJndiName();
                String mdLink = mdRef[i].getMessageDestinationLink();
                String mdType = mdRef[i].getMessageDestinationType();
                String mdUsage = mdRef[i].getMessageDestinationUsage();
                if (logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG, "" + jndiName + " " + mdLink + " " + mdType + " " + mdUsage);
                }
                if (mdLink != null && jndiName == null) {
                    String mdName = getMDJndiName(url, mdLink, mdType, mdUsage, dd);
                    mdRef[i].setJndiName(mdName);
                }
            }

        }

        // ... and put it in cache if ear case.
        if (earLoader != null) {
            // case of ear application.
            urlJarBindings.put(filename, dd);
        }

        return dd;
    }


    /**
     * Return the port component desc from the pcLink string.
     * pcLink format : filename.[jar or war]#portComponentName in the same Ear File
     *                 portComponentName
     *
     * @param ejbjarURL the url of the ejbjar being parsed. This is needed
     * because pcLink is relative. With the url and the pcLink, we can
     * know where the file is located.
     * @param pcLink the pcLink tag of an port-component-ref.
     * @param earLoader the classloader of the ear.
     * @param moduleLoader the classloader of the current module
     *
     * @return the pcLink portComponent.
     *
     * @throws WSDeploymentDescException when it failed
     */
    private PortComponentDesc getPCDesc(final URL ejbjarURL,
                                        final String pcLink,
                                        final ClassLoader moduleLoader,
                                        final ClassLoader earLoader)
        throws WSDeploymentDescException {

        // now ask WS Manager for port-component-desc
        return wsDDManager.getPortComponentDesc(ejbjarURL, pcLink, moduleLoader, earLoader);
    }


    /**
     * Return the JNDI name from the ejbLink string.
     * ejbLink format : filename.jar#beanName in the same Ear File
     *                  beanName in the same ejb-jar file.
     * @param currentFile the url of the jar being parsed. This is needed because
     * ejbLink is relative. With the url and the ejbLink, we can know where
     * the file is locate.
     * @param ejbLink the ejbLink tag of an ejb-ref
     * @param earCl optionnal classloader
     * @param ejbType the type of the referenced ejb in the ejb-ref tag.
     * @param deploymentDesc the deployment descriptor of the parsed deploymentDesc.
     * @param isEjbRef true if the jndi name to resolve is an ejb-ref
     * @return the JNDI name if found, null otherwise
     * @throws DeploymentDescException when it failed
     */
    public String getJndiName(final URL currentFile, final String ejbLink, final ClassLoader earCl, final String ejbType,
                               final DeploymentDesc deploymentDesc, final boolean isEjbRef)
        throws DeploymentDescException {
        // Extract from the ejb link
        //   - the name of the file
        //   - the name of the bean
        String ejbJarLink = null;
        String beanNameLink = null;

        //Same jar ?
        if (ejbLink.indexOf(LINK_SEPARATOR) == -1) {
            BeanDesc bd = null;
            //Link to a bean inside the same ejb-jar file.
            if (earCl == null && deploymentDesc == null) {
                    throw new DeploymentDescException("Deployment desc for file ejb-jar '" + currentFile + "' not found");
            }
            // Read in its own deployment descriptor if not null
            if (deploymentDesc != null) {
                bd =  deploymentDesc.getBeanDesc(ejbLink);
            }
            String url = URLUtils.urlToFile(currentFile).getPath();
            URLClassLoader earClassLoader = null;
            // get the loader for classes (ejb classloader).
            URLClassLoader ejbLoader = (URLClassLoader) urlEjbCLBindings.get(url);
            if (earCl != null) {
                earClassLoader = (URLClassLoader) earCl;
                //Add it
                urlEarCLBindings.put(url, earCl);
            } else {
                earClassLoader = (URLClassLoader) urlEarCLBindings.get(url);
            }

            //ejb standalone case
            if ((earClassLoader == null) && (bd == null)) {
                String err = "Ejb-link " + ejbLink + " not found inside the file " + currentFile + ". The bean doesn't exists.";
                throw new DeploymentDescException(err);
            }

            // ear case, search in all ejbjars
            if (bd == null) {
                if (logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG, "The bean '" + ejbLink + "' was not found in the current ejbjar, searching on all the ejbjars.");
                }
                Vector v = (Vector) earCLEjbLinkJar.get(earClassLoader);
                DeploymentDesc lookupDD = null;
                String fileName = null;
                URL urlRead = null;
                String urlReadString = null;
                boolean found = false;
                BeanDesc bdtmp = null;
                for (Enumeration e = v.elements(); e.hasMoreElements();) {
                    urlReadString = (String) e.nextElement();
                    File f = new File(urlReadString);
                    urlRead = URLUtils.fileToURL(f);
                    // Classloader is null ? try to get it
                    if (ejbLoader == null) {
                        ejbLoader = (URLClassLoader) urlEjbCLBindings.get(f.getPath());
                    }
                    // if it is still null, fails
                    if (ejbLoader == null) {
                        throw new DeploymentDescException("Error while resolving ejb-link. The ejb classloader is not found for url '" + urlReadString + "'.");
                    }

                    // Add it
                    urlEjbCLBindings.put(urlReadString, ejbLoader);

                    fileName = URLUtils.urlToFile(urlRead).getPath();
                    //do not analyse current file
                    if (!fileName.equals(url)) {
                        //Need to load deployment desc without any resolving link
                        // Get the instance of the DeploymentDesc.
                        if (f.isDirectory()) {
                            lookupDD = getDeploymentDescriptor(fileName + EJB_JAR_FILE_NAME,
                                                               BeanNaming.getJonasXmlName(fileName + EJB_JAR_FILE_NAME),
                                                               ejbLoader,
                                                               f.getAbsolutePath());
                        } else if (fileName.toLowerCase().endsWith(".xml")) {
                            // This is an XML file name: Treat it for upward compatibility
                            // f is the ejb-jar.xml file, first paretn is the META-INF directory, and second parent is the root directory of the module
                            File parent = f.getParentFile().getParentFile();
                            lookupDD = getDeploymentDescriptor(fileName,
                                                               BeanNaming.getJonasXmlName(fileName),
                                                               ejbLoader,
                                                               parent.getAbsolutePath());
                        } else {
                            // This is an ejb-jar
                            // Check if there is an altenate DD for this URL (ear only)
                            String altname = null;
                            URL altDDUrl = (URL) urlAltDDBindings.get(url);
                            if (altDDUrl != null) {
                                altname = URLUtils.urlToFile(altDDUrl).getPath();
                            }
                            lookupDD = getDeploymentDescriptor(fileName,
                                                               ejbLoader,
                                                               altname);
                        }

                        if (lookupDD != null) {
                            bdtmp = lookupDD.getBeanDesc(ejbLink);
                            if (bdtmp != null) {
                                if (logger.isLoggable(BasicLevel.DEBUG)) {
                                    logger.log(BasicLevel.DEBUG, "Found a BeanDesc in the Deployment Desc." + urlRead);
                                }
                                //ejblink was found ?
                                if (found) {
                                    String err = "There are more than one bean with the name '" + ejbLink + "' which were found in all the ejbjars of this EAR.";
                                    throw new DeploymentDescException(err);
                                } else {
                                    found = true;
                                    bd = bdtmp;
                                }
                            } else {
                                if (logger.isLoggable(BasicLevel.DEBUG)) {
                                    logger.log(BasicLevel.DEBUG, "No BeanDesc found in the Deployment Desc." + urlRead);
                                }
                            }
                        }
                    }
                }
                if (!found) {
                    String err = "No ejblink was found for '" + ejbLink + "' in all the ejbjars of this EAR.";
                    throw new DeploymentDescException(err);
                }
            }

            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "BeanDesc found = " + bd.getEjbName());
            }
            //Check if the type of the ejb-ref is correct.
            checkType(currentFile, ejbType, bd);

            if (bd == null) {
                String err = "Ejb-link " + ejbLink + " not found inside the file " + currentFile + ". The bean doesn't exists.";
                throw new DeploymentDescException(err);
            }

            String jndiname;
            if (isEjbRef) {
                jndiname = bd.getJndiName();
            } else {
                jndiname = bd.getJndiLocalName();
            }
            return jndiname;
        }

        if (earCl != null) {
            //Add it
            urlEarCLBindings.put(URLUtils.urlToFile(currentFile).getPath(), earCl);
        }

        //Ejb-link not in the same ejb-jar file.
        StringTokenizer st = new StringTokenizer(ejbLink, LINK_SEPARATOR);

        // We must have only two elements after this step, one for the fileName
        // before the # and the name of the bean after the # char
        if (st.countTokens() != 2 || ejbLink.startsWith(LINK_SEPARATOR)
            || ejbLink.endsWith(LINK_SEPARATOR)) {

            String err = "Ejb link " + ejbLink + " has a bad format. Correct format :  filename.jar#beanName.";
            throw new DeploymentDescException(err);
        }

        //Get the token
        ejbJarLink = st.nextToken();
        beanNameLink = st.nextToken();

        //Check if ejbJarLink is a jar or not
        if (!ejbJarLink.endsWith(".jar")) {
            String err = "Ejbjar filename " + ejbJarLink + " from the ejb-link " + ejbLink + " has a bad format. Correct format :  filename.jar";
            throw new DeploymentDescException(err);
        }


        // Now construct the URL from the absolute path from the url ejbJar and
        // the relative path from ejbJarLink
        URL ejbJarLinkUrl = null;
        ejbJarLinkUrl = URLUtils.fileToURL(new File(URLUtils.urlToFile(currentFile).getParent() + File.separator + ejbJarLink));

        // We've got the url
        BeanDesc bd = null;
        // The ejb-link with .jar#beanName could reference the current jar file
        if (currentFile.getPath().equalsIgnoreCase(ejbJarLinkUrl.getPath())) {
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "ejblink jar#bean reference our current file");
            }

            // Read in its own deployment descriptor if not null
            if (deploymentDesc != null) {
                bd =  deploymentDesc.getBeanDesc(beanNameLink);
            } else {
                if (logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG, "DD = null, cannot return bean in the current DD");
                }
            }

        } else {
            //Another file :

            // Now, We can ask the Deployment Descriptor of this url
            DeploymentDesc dd = getDeploymentDesc(currentFile, ejbJarLinkUrl);

            // JndiName resolve.
            bd = dd.getBeanDesc(beanNameLink);
        }

        if (bd == null) {
            String err = "Ejb-link " + ejbLink + " not found inside the file " + currentFile + ". The bean doesn't exists";
            throw new DeploymentDescException(err);
        }

        //Check if the type of the ejb-ref is correct.
        checkType(currentFile, ejbType, bd);

        String jndiname;
        if (isEjbRef) {
            jndiname = bd.getJndiName();
        } else {
            jndiname = bd.getJndiLocalName();
        }
        return jndiname;
    }

    /**
     * Return the JNDI name from the messageDestinationLink string.
     * messageDestinationLink format : filename.jar#beanName in the same Ear File
     *                                 beanName in the same ejb-jar file.
     * @param ejbJarURL the url of the jar being parsed. This is needed because
     * mdLink is relative. With the url and the mdLink, we can know where
     * the file is locate.
     * @param mdLink the mdLink tag of a message-destination-ref
     * @param mdType the type of the referenced mdb in the message-destination-ref tag.
     * @param mdUsage the usage of the referenced mdb in the message-destination-ref tag.
     * @param deploymentDesc the deployment descriptor of the parsed deploymentDesc.
     * @return the JNDI name if found, null otherwise
     * @throws DeploymentDescException when it failed
     */
    private String getMDJndiName(final URL ejbJarURL,
                                 final String mdLink,
                                 final String mdType,
                                 final String mdUsage,
                                 final DeploymentDesc deploymentDesc)
        throws DeploymentDescException {

        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "" + ejbJarURL + " " + mdLink + " " + mdType + " " + mdUsage);
        }
        // Extract from the mdb link
        //   - the name of the file
        //   - the name of the destination
        String ejbJarLink = null;
        DeploymentDesc dd = deploymentDesc;

        //Different jar ?
        if (mdLink.indexOf(LINK_SEPARATOR) != -1) {
            //Message-destination-link not in the same ejb-jar file.
            StringTokenizer st = new StringTokenizer(mdLink, LINK_SEPARATOR);

            // We must have only two elements after this step, one for the fileName
            // before the # and the name of the message-destination after the # char
            if (st.countTokens() != 2 || mdLink.startsWith(LINK_SEPARATOR)
                || mdLink.endsWith(LINK_SEPARATOR)) {

                String err = "Message-destination-link " + mdLink + " has a bad format. Correct format :  filename.jar#messageDestinationName.";
                throw new DeploymentDescException(err);
            }

            //Get the token
            ejbJarLink = st.nextToken();

            //Check if ejbJarLink is a jar or not
            if (!ejbJarLink.endsWith(".jar")) {
                String err = "Ejbjar filename " + ejbJarLink + " from the message-destination-link " + mdLink + " has a bad format. Correct format :  filename.jar";
                throw new DeploymentDescException(err);
            }


            // Now construct the URL from the absolute path from the url ejbJar and
            // the relative path from ejbJarLink
            URL ejbJarLinkUrl = null;
            try {
                File ejbJarFile = URLUtils.urlToFile(ejbJarURL);
                File ejbJarLinkFile = new File(ejbJarFile.getParentFile(), ejbJarLink).getCanonicalFile();
                ejbJarLinkUrl = URLUtils.fileToURL(ejbJarLinkFile);
            } catch (IOException ioe) {
                String err = "Error when creating/accessing a file. Error :" + ioe.getMessage();
                throw new DeploymentDescException(err, ioe);
            }

            // Check if the jar exist.
            if (!URLUtils.urlToFile(ejbJarLinkUrl).exists()) {
                String err = "Cannot get the deployment descriptor for '" + ejbJarLinkUrl + "'. The file doesn't exist.";
                throw new DeploymentDescException(err);
            }

            // We've got the url
            // Now, We can ask the Deployment Descriptor of this url
            dd = getDeploymentDesc(ejbJarURL, ejbJarLinkUrl);
        }

        //Link to a destination inside the same ejb-jar file.
        if (dd == null) {
            throw new DeploymentDescException("Deployment desc for file ejb-jar '" + ejbJarURL + "' not found");
        }

        boolean foundMd = dd.getMessageDestination(mdLink);
        if (!foundMd) {
            String err = "No message-destination was found for '" + mdLink + "' in the ejbjar specified.";
            throw new DeploymentDescException(err);
        }

        JonasMessageDestination md = dd.getJonasMessageDestination(mdLink);

        if (md == null) {
            String err = "No jonas-message-destination was found for '" + mdLink + "' in the ejbjar specified.";
            throw new DeploymentDescException(err);
        }

        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "Message-destination found = " + md.getJndiName());
        }

        //Check if the type & usage of the message-destination-ref is correct.
        //checkTypeUsage(ejbJarURL, mdType, mdUsage, mdd);

        return md.getJndiName();

    }

    /**
     * Check if the link is available in the case of an ear application.
     * @param earClassLoader the ear classloader of the ear application.
     * @param url the url to check.
     * @throws DeploymentDescException if the ejb-link isn't available.
     */
    private void checkEjbLinkAvailable(final ClassLoader earClassLoader,
                                       final URL url)
        throws DeploymentDescException {

        Vector v = (Vector) earCLEjbLinkJar.get(earClassLoader);

        if (v != null) {
            if (!v.contains(URLUtils.urlToFile(url).getPath())) {
                throw new DeploymentDescException("The ejb-link or message-destination-link of '" + url
                        + "' must be done on an ejb-jar defined in the ear application "
                        + "(application.xml <module><ejb>###</ejb></module>)");
            }
        } else {
            String err = "setAvailableEjbLinkJar was badly called.";
            throw new DeploymentDescException(err);
        }
    }

    /**
     * Set for the given ear identified by its earClassLoader the list of the
     * ejb-jar that can be in the ejb-link and the optional Desployment Desc.
     * @param earClassLoader the classloader of the ear application.
     * @param urls the list of the URLs of the ear application that can be in
     * the ejb-link.
     * @param altDDs the list of the URLs of the alternate DDs to use if specified.
     */
    public void setAvailableEjbJarsAndAltDDs(final ClassLoader earClassLoader,
                                             final URL[] urls,
                                             final URL[] altDDs) {
        Vector v = new Vector();
        for (int i = 0; i < urls.length; i++) {
            v.addElement(URLUtils.urlToFile(urls[i]).getPath());
            if (altDDs[i] != null) {
                urlAltDDBindings.put(URLUtils.urlToFile(urls[i]).getPath(), altDDs[i]);
            }
        }
        earCLEjbLinkJar.put(earClassLoader, v);
    }

    /**
     * Make a cleanup of the cache of deployment descriptor. This method must
     * be invoked after the ear deployment by the EAR service.
     * @param earClassLoader the URLClassLoader of the ear application to
     * remove from the cache.
     */
    public void removeCache(final ClassLoader earClassLoader) {
        Vector v = (Vector) earCLEjbLinkJar.remove(earClassLoader);
        if (v != null) {
            for (int i = 0; i < v.size(); i++) {
                String url = (String) v.elementAt(i);
                urlJarBindings.remove(url);
                urlEarCLBindings.remove(url);
                urlEjbCLBindings.remove(url);
                urlAltDDBindings.remove(url);
            }
        }

        if (earCLEjbLinkJar.size() != 0
            || urlJarBindings.size() != 0
            || urlEarCLBindings.size() != 0
            || urlAltDDBindings.size() != 0
            || urlEjbCLBindings.size() != 0) {

            String buffer = earCLEjbLinkJar.size() + " ";
            buffer += urlJarBindings.size() + " ";
            buffer += urlEarCLBindings.size() + " ";
            buffer += urlEjbCLBindings.size() + " ";
            buffer += urlAltDDBindings.size();

            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, buffer + " there are some elements in cache");
            }
        }
    }

    /**
     * Get the size of the cache (number of entries in the cache).
     * Used only for debugging.
     * @return the size of the cache (number of entries in the cache).
     */
    public int getCacheSize() {
        return earCLEjbLinkJar.size() + urlJarBindings.size()
            + urlEarCLBindings.size() + urlEjbCLBindings.size()
            + urlAltDDBindings.size();
    }

    /**
     * Return a string representation of the cache. (Used only for debugging).
     * @return a string representation of the cache.
     */
    @Override
    public String toString() {
        return earCLEjbLinkJar.size() + " " + urlJarBindings.size() + " "
            + urlEarCLBindings.size() + " " + urlEjbCLBindings.size() + " "
            + urlAltDDBindings.size();
    }

    /**
     * Factory method using deployment descriptor and Jonas deployment descriptor
     * file names.
     * used by GEnIC or GenIDL
     * @param ejbJarXmlFileName name of the standard DD
     * @param jonasEjbJarXmlFileName name of the specific DD
     * @param jarFileName name of the jar file
     * @return instance of the corresponding DeploymentDesc
     * @exception DeploymentDescException when DeploymentDesc cannot be created with
     * given files.
     */
    public static DeploymentDesc getDeploymentDesc(final String ejbJarXmlFileName,
                                                   final String jonasEjbJarXmlFileName,
                                                   final String jarFileName)
        throws DeploymentDescException {
        // instantiate deployment descriptor
        ClassLoader cl = DeploymentDesc.class.getClassLoader();
        if (cl == null) {
            cl = Thread.currentThread().getContextClassLoader();
        }
        return getDeploymentDescriptor(ejbJarXmlFileName, jonasEjbJarXmlFileName, cl, jarFileName);
    }

    /**
     * Factory method using deployment descriptor and Jonas deployment descriptor
     * file names. (input is 2 .xml descriptors)
     * @param ejbJarXmlFileName Name of the xml for the EJBJAR
     * @param jonasEjbJarXmlFileName Name of the xml for JOnAS ejbjar
     * @param cl classloader to use
     * @param moduleDirName name of file
     * @return instance of the corresponding DeploymentDesc
     * @exception DeploymentDescException when DeploymentDesc cannot be created with
     * given files.
     */
    private static DeploymentDesc getDeploymentDescriptor(final String ejbJarXmlFileName,
                                                          final String jonasEjbJarXmlFileName,
                                                          final ClassLoader cl,
                                                          final String moduleDirName)
        throws DeploymentDescException {

        InputStream is;

        // load deployment descriptor data
        try {
            is = new FileInputStream(ejbJarXmlFileName);
            // store file content in a String
            xmlContent = xmlContent(is);
            // reposition to the begining of the stream
            is = new FileInputStream(ejbJarXmlFileName);
        } catch (FileNotFoundException e) {
            throw new DeploymentDescException(ejbJarXmlFileName + " file not found");
        } catch (IOException ioe) {
            throw new DeploymentDescException("Cannot read the content of the xml file " + ejbJarXmlFileName);
        }
        EjbJar ejbJar = loadEjbJar(new InputStreamReader(is), ejbJarXmlFileName);
        String dtdversion = ejbJar.getVersion();
        try {
            is.close();
        } catch (IOException e) {
            logger.log(BasicLevel.WARN, "Can't close '" + ejbJarXmlFileName + "'");
        }

        // load jonas deployment descriptor data
        try {
            is = new FileInputStream(jonasEjbJarXmlFileName);
            // store file content in a String
            jonasXmlContent = xmlContent(is);
            // reposition to the begining of the stream
            is = new FileInputStream(jonasEjbJarXmlFileName);
        } catch (FileNotFoundException e) {
            throw new DeploymentDescException(jonasEjbJarXmlFileName + " file not found");
        } catch (IOException ioe) {
            throw new DeploymentDescException("Cannot read the content of the xml file " + ejbJarXmlFileName);
        }

        JonasEjbJar jonasEjbJar = loadJonasEjbJar(new InputStreamReader(is),
                                                  jonasEjbJarXmlFileName);
        try {
            is.close();
        } catch (IOException e) {
            logger.log(BasicLevel.WARN, "Can't close '" + jonasEjbJarXmlFileName + "'");
        }

        // instantiate deployment descriptor
        DeploymentDesc descEjb = null;
        if (dtdversion.equals("1.1")) {
            descEjb = new DeploymentDescEjb1_1(cl, ejbJar, jonasEjbJar, logger, moduleDirName);
        } else {
            descEjb = new DeploymentDescEjb2(cl, ejbJar, jonasEjbJar, logger, moduleDirName);
        }
        descEjb.setXmlContent(xmlContent);
        descEjb.setJOnASXmlContent(jonasXmlContent);
        return descEjb;
    }


    /**
     * Factory method using the ejb-jar file name. (input is a .jar file)
     * Called either from GenIC or from createContainer.
     * @param ejbJarFileName name of the ejbjar
     * @param cl classloader to use
     * @param altWebXmlFilename null if not ear
     * @return instance of the corresponding DeploymentDesc
     * @exception DeploymentDescException when DeploymentDesc cannot be created with
     * given ejb-jar file.
     */
    private static DeploymentDesc getDeploymentDescriptor(final String ejbJarFileName,
                                                          final ClassLoader cl,
                                                          final String altWebXmlFilename)
        throws DeploymentDescException {

        IDeploymentDescReader reader = null;
        InputStream isDd = null;
        InputStream isJdd = null;
        EjbJar ejbJar;
        JonasEjbJar jonasEjbJar;

        // Check if the Alt deploymentDesc file exists. (optional value)
        if ((altWebXmlFilename != null) && (!new File(altWebXmlFilename).exists())) {
            String err = "The file for the altdd tag for the EAR case '" + altWebXmlFilename + "' was not found.";
            throw new DeploymentDescException(err);
        }

        // Get the XML DD files of the ejb-jar as InputStream
        try {

            // Prepare DD reader
            File ejbJarFile = new File(ejbJarFileName);
            if (ejbJarFile.isDirectory()) {
                reader = new DirectoryDeploymentDescReader(ejbJarFile);
            } else {
                reader = new JarDeploymentDescReader(ejbJarFile);
            }

            // Read standard DD
            if (altWebXmlFilename == null) {
                // No alt-dd case ( standard)
                isDd = reader.getStandardDeploymentDescriptor();
                if (isDd != null) {
                    xmlContent = xmlContent(isDd);
                    isDd = reader.getStandardDeploymentDescriptor();
                }
            } else {
                // AltDD (Ear and optional)
                isDd = new FileInputStream(altWebXmlFilename);
                xmlContent = xmlContent(isDd);
                isDd = new FileInputStream(altWebXmlFilename);
            }

            // Read JOnAS DD
            isJdd = reader.getJOnASDeploymentDescriptor();
            if (isJdd != null) {
                jonasXmlContent = xmlContent(isJdd);
                isJdd = reader.getJOnASDeploymentDescriptor();
            } else {
                logger.log(BasicLevel.WARN, "No entry '" + JONAS_EJB_JAR_FILE_NAME + "' was found in the file '" + ejbJarFileName + "'.");
            }
        } catch (Exception e) {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException i) {
                    logger.log(BasicLevel.WARN, "Can't close '" + ejbJarFileName + "'");
                }
            }
            logger.log(BasicLevel.ERROR, "Cannot read the XML deployment descriptors for " + ejbJarFileName + ":", e);
            throw new DeploymentDescException("Cannot read the XML deployment descriptors for " + ejbJarFileName + ":" + e);
        }

        // load standard deployment descriptor data
        String dtdversion = null;
        if (isDd != null) {
            ejbJar = loadEjbJar(new InputStreamReader(isDd), EJB_JAR_FILE_NAME);
            dtdversion = ejbJar.getVersion();
            try {
                isDd.close();
            } catch (IOException e) {
                logger.log(BasicLevel.WARN, "Can't close META-INF/ejb-jar.xml in '" + ejbJarFileName + "'");
            }
        } else {
            ejbJar = new EjbJar();
        }

        // load jonas deployment descriptor data
        if (isJdd != null) {
            jonasEjbJar = loadJonasEjbJar(new InputStreamReader(isJdd),
                                          JONAS_EJB_JAR_FILE_NAME);
            try {
                isJdd.close();
            } catch (IOException e) {
                logger.log(BasicLevel.WARN, "Can't close META-INF/jonas-ejb-jar.xml in '" + ejbJarFileName + "'");
            }

        } else {
            jonasEjbJar = new JonasEjbJar();
        }

        // Close the resources
        if (reader != null) {
            try {
                reader.close();
            } catch (IOException e) {
                logger.log(BasicLevel.WARN, "Can't close '" + ejbJarFileName + "'");
            }
        }

        // instantiate deployment descriptor
        DeploymentDesc descEjb = null;
        if ("1.1".equals(dtdversion)) {
            descEjb = new DeploymentDescEjb1_1(cl, ejbJar, jonasEjbJar, logger, ejbJarFileName);
        } else {
            descEjb = new DeploymentDescEjb2(cl, ejbJar, jonasEjbJar, logger, ejbJarFileName);
        }
        descEjb.setXmlContent(xmlContent);
        descEjb.setJOnASXmlContent(jonasXmlContent);
        return descEjb;
    }


   /**
     * Load the ejb_jar.xml file.
     * @param reader the reader of the XML file.
     * @param name the name of the file (ejb-jar.xml).
     * @return a structure containing the result of the ejb-jar.xml parsing.
     * @throws DeploymentDescException if the deployment descriptor
     * is corrupted.
     */
    public static EjbJar loadEjbJar(final Reader reader, final String name)
        throws DeploymentDescException {
        EjbJar ejbjar = new EjbJar();

        // Create if null
        if (ejbjarDigester == null) {
            // Create and initialize the digester
            ejbjarDigester = new JDigester(ejbjarRuleSet,
                                           parsingWithValidation,
                                           true,
                                           new EjbjarDTDs(),
                                           new EjbjarSchemas());
        }
        try {
            ejbjarDigester.parse(reader, name, ejbjar);
        } finally {
            ejbjarDigester.push(null);
        }
        return ejbjar;
    }

    /**
     * Load the EjbJar file
     * @param reader reader containing the stream
     * @param name name of the file
     * @return JOnAS DD object
     * @throws DeploymentDescException if loading fails
     */
    public static JonasEjbJar loadJonasEjbJar(final Reader reader,
                                               final String name)
        throws DeploymentDescException {
         JonasEjbJar jonasEjbjar = new JonasEjbJar();
        // Create if null
        if (jonasEjbjarDigester == null) {
            // Create and initialize the digester
            jonasEjbjarDigester = new JDigester(jonasEjbjarRuleSet,
                                           parsingWithValidation,
                                           true,
                                           new JonasEjbjarDTDs(),
                                           new JonasEjbjarSchemas());
        }
        try {
            jonasEjbjarDigester.parse(reader , name, jonasEjbjar);
        } catch (DeploymentDescException  e) {
            throw e;
        } finally {
            jonasEjbjarDigester.push(null);
        }
        return jonasEjbjar;
    }

    /**
     * Controls whether the parser is reporting all validity errors.
     * @return if true, all external entities will be read.
     */
    public static boolean getParsingWithValidation() {
        return parsingWithValidation;
    }

    /**
     * Controls whether the parser is reporting all validity errors.
     * @param validation if true, all external entities will be read.
     */
    public static void setParsingWithValidation(final boolean validation) {
        EjbDeploymentDescManager.parsingWithValidation = validation;
    }

    /**
     * Return the content of the web.xml file
     * @return the content of the web.xml file
     */
    public static String getXmlContent() {
        return xmlContent;
    }

    /**
     * Return the content of the jonas-web.xml file
     * @return the content of the jonas-web.xml file
     */
    public static String getJOnASXmlContent() {
        return jonasXmlContent;
    }


    /**
     * Check if the type of the ejb-ref is correct.
     * @param ejbJar the URL of the ejb-jar being parsed.
     * @param ejbType the type of the ejb-ref (ejb-ref-type).
     * @param bd the descriptor of the referenced bean.
     * @throws DeploymentDescException if the type is incorrect.
     */
    protected void checkType(final URL ejbJar, final String ejbType, final IJNDIEnvRefsGroupDesc bd) throws DeploymentDescException {

        if (bd instanceof SessionDesc) {
            if (!ejbType.equalsIgnoreCase("Session")) {
                String err = "Deployment desc '" + ejbJar
                        + "' has an incompatible ejb-ref-type: Required Session but found " + ejbType;
                throw new DeploymentDescException(err);
            }
        } else if (bd instanceof EntityDesc) {
            if (!ejbType.equalsIgnoreCase("Entity")) {
                String err = "Deployment desc '" + ejbJar
                        + "' has an incompatible ejb-ref-type: Required Entity but found " + ejbType;
                throw new DeploymentDescException(err);
            }
        } else {
            String err = "Deployment desc '" + ejbJar + "' has a bad ejb-ref-type.";
            throw new DeploymentDescException(err);
        }
    }

    /**
     * Check if the type & usage of the message-destination-ref is correct.
     * @param url the URL of the file being parsed.
     * @param mdType the type of the message-destination-ref
     *        (message-destination-type).
     * @param mdUsage the usage of the message-destination-ref
     *        (message-destination-usage).
     * @param bd the descriptor of the referenced bean.
     * @throws DeploymentDescException if the type is incorrect.
     */
    protected void checkTypeUsage(final URL url, final String mdType, final String mdUsage, final BeanDesc bd) throws DeploymentDescException {

        if (bd instanceof MessageDrivenDesc) {
            MessageDrivenDesc mdd = (MessageDrivenDesc) bd;
            if (mdd.getDestinationType().equals("javax.jms.Topic")) {
                if (!mdType.equalsIgnoreCase("javax.jms.Topic")) {
                    String err = "Deployment desc '" + url
                            + "' has an incompatible message-destination-type: Required javax.jms.Topic but found "
                            + mdType;
                    throw new DeploymentDescException(err);
                }
            } else if (mdd.getDestinationType().equals("javax.jms.Queue")) {
                if (!mdType.equalsIgnoreCase("javax.jms.Queue")) {
                    String err = "Deployment desc '" + url
                            + "' has an incompatible message-destination-type: Required javax.jms.Queue but found "
                            + mdType;
                    throw new DeploymentDescException(err);
                }
            }
        } else {
            String err = "Deployment desc '" + url + "' has a bad message-destination-ref-type.";
            throw new DeploymentDescException(err);
        }
    }

    /**
     * Add a mapping between url and classloader
     * @param ejbClassloader EjbClassloader on which associate URLs
     * @param urls array of urls associated to the classloader
     */
    public void addClassLoaderUrlMapping(final ClassLoader ejbClassloader, final URL[] urls) {
        for (int u = 0; u < urls.length; u++) {
            urlEjbCLBindings.put(urls[u].getPath(), ejbClassloader);
        }
    }

    /**
     * Super simple interface that hides jar/directory.
     * Used this in order to not introduce a big change with IArchive support.
     */
    private interface IDeploymentDescReader {
        InputStream getStandardDeploymentDescriptor() throws IOException;
        InputStream getJOnASDeploymentDescriptor() throws IOException;
        void close() throws IOException;

    }

    /**
     * Dedicated to Directory structured as Jar handling.
     */
    private static class DirectoryDeploymentDescReader implements IDeploymentDescReader {
        private File directory;

        public DirectoryDeploymentDescReader(File ejbJarFile) {
            this.directory = ejbJarFile;
        }

        public InputStream getStandardDeploymentDescriptor() throws IOException {
            File metaInf = new File(directory, "META-INF");
            File ejbJarXmlFile = new File(metaInf, "ejb-jar.xml");
            if (ejbJarXmlFile.isFile()) {
                return new FileInputStream(ejbJarXmlFile);
            }
            return null;
        }

        public InputStream getJOnASDeploymentDescriptor() throws IOException {
            File metaInf = new File(directory, "META-INF");
            File jonasEjbJarXmlFile = new File(metaInf, "jonas-ejb-jar.xml");
            if (jonasEjbJarXmlFile.isFile()) {
                return new FileInputStream(jonasEjbJarXmlFile);
            }
            return null;
        }

        public void close() throws IOException {
            // Nothing to close
        }
    }

    /**
     * Dedicated to Jar file handling.
     */
    private static class JarDeploymentDescReader implements IDeploymentDescReader {
        private JarFile jarFile;

        public JarDeploymentDescReader(File ejbJarFile) throws IOException {
            this.jarFile = new JarFile(ejbJarFile);
        }

        public InputStream getStandardDeploymentDescriptor() throws IOException {
            JarEntry entry = jarFile.getJarEntry(EJB_JAR_FILE_NAME);
            if (entry != null) {
                return jarFile.getInputStream(entry);
            }
            return null;
        }

        public InputStream getJOnASDeploymentDescriptor() throws IOException {
            JarEntry entry = jarFile.getJarEntry(JONAS_EJB_JAR_FILE_NAME);
            if (entry != null) {
                return jarFile.getInputStream(entry);
            }
            return null;
        }

        public void close() throws IOException {
            this.jarFile.close();
        }
    }
}
