/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-2005 Bull S.A.
 * Contact: jonas-team@objectweb.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$
 * --------------------------------------------------------------------------
 */

package org.objectweb.jonas.container;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

import javax.management.MBeanException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.OperationsException;
import javax.management.modelmbean.ModelMBean;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.apache.commons.modeler.ManagedBean;
import org.apache.commons.modeler.Registry;
import org.objectweb.carol.util.configuration.ConfigurationRepository;
import org.objectweb.carol.util.configuration.ProtocolConfiguration;
import org.objectweb.jonas.common.JModule;
import org.objectweb.jonas.common.JProp;
import org.objectweb.jonas.common.Log;
import org.objectweb.jonas.ear.EarServiceImpl;
import org.objectweb.jonas.jms.JmsService;
import org.objectweb.jonas.jmx.J2eeObjectName;
import org.objectweb.jonas.jmx.JmxService;
import org.objectweb.jonas.jmx.JonasObjectName;
import org.objectweb.jonas.jtm.TransactionService;
import org.objectweb.jonas.management.JonasMBeanTools;
import org.objectweb.jonas.naming.CompNamingContext;
import org.objectweb.jonas.naming.NamingManager;
import org.objectweb.jonas.security.SecurityService;
import org.objectweb.jonas.server.LoaderManager;
import org.objectweb.jonas.server.WorkManagerMBean;
import org.objectweb.jonas.service.AbsServiceImpl;
import org.objectweb.jonas.service.ServiceException;
import org.objectweb.jonas.service.ServiceManager;
import org.objectweb.jonas_ejb.container.BeanFactory;
import org.objectweb.jonas_ejb.container.Container;
import org.objectweb.jonas_ejb.container.JContainer;
import org.objectweb.jonas_ejb.container.JEntityFactory;
import org.objectweb.jonas_ejb.container.JFactory;
import org.objectweb.jonas_ejb.container.JSessionFactory;
import org.objectweb.jonas_ejb.container.JStatelessFactory;
import org.objectweb.jonas_ejb.container.PermissionManager;
import org.objectweb.jonas_ejb.deployment.api.BeanDesc;
import org.objectweb.jonas_ejb.deployment.api.DeploymentDesc;
import org.objectweb.jonas_ejb.deployment.api.EntityBmpDesc;
import org.objectweb.jonas_ejb.deployment.api.EntityCmpDesc;
import org.objectweb.jonas_ejb.deployment.api.SessionStatefulDesc;
import org.objectweb.jonas_ejb.deployment.api.SessionStatelessDesc;
import org.objectweb.jonas_ejb.deployment.lib.wrapper.EjbManagerWrapper;
import org.objectweb.jonas_jms.api.JmsManager;
import org.objectweb.jonas_lib.JWorkManager;
import org.objectweb.jonas_lib.deployment.api.DeploymentDescException;
import org.objectweb.jonas_lib.deployment.work.CleanerException;
import org.objectweb.jonas_lib.deployment.work.DeployerLog;
import org.objectweb.jonas_lib.deployment.work.DeployerLogException;
import org.objectweb.jonas_lib.deployment.work.FileManager;
import org.objectweb.jonas_lib.deployment.work.JarCleanTask;
import org.objectweb.jonas_lib.deployment.work.WorkCleaner;
import org.objectweb.jonas_lib.files.FileUtils;
import org.objectweb.jonas_lib.files.WsGenChecker;
import org.objectweb.jonas_lib.loader.EjbJarClassLoader;
import org.objectweb.jonas_lib.version.Version;
import org.objectweb.transaction.jta.TransactionManager;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;

/**
 * Implementation of the EJB Container Service for jonas. This class works only
 * with jonas_ejb classes.
 * @author Philippe Coq
 * @author Jeff Mesnil (Security)
 * @author Markus Karg (Novell port)
 * @author Christophe Ney (for making easier Enhydra integration)
 * @author Adriana Danes (complete management methods)
 * @author Florent Benoit & Ludovic Bert (Ear service, deployJars, undeployJars)
 * @author Benjamin Bonnet (max thread pool size)
 * @author Michel-Ange Anton (JSR77 MBean : EJBModule)
 * @author Adriana Danes (JSR77)
 */
public class EJBServiceImpl extends AbsServiceImpl implements EJBService, EJBServiceImplMBean {

    /** server logger */
    private static Logger servlog = null;

    /** loader logger */
    private static Logger loaderlog = null;

    /**
     * Transaction manager reference (tm can be accessed directly from
     * everywhere in the package.)
     */
    private TransactionManager tm = null;

    /** JMS manager reference */
    private JmsManager jms = null;

    /** JMX Server */
    private MBeanServer mbeanServer = null;

    /** Security service reference */
    private SecurityService securityService = null;

    /**
     * Reference to the cleaner
     */
    private static WorkCleaner workCleaner = null;

    /**
     * Modeler's MBean definition Registry
     */
    private Registry oRegistry = null;

    /**
     * List of the ejb names to load when starting the EJB Container Service
     */
    private Vector ejbNames = new Vector();

    /** We have a Container by ejb-jar file. */
    private Vector containers = new Vector();

    /**
     * Auto-GenIC is enabled or disabled ?
    */
    public static final String AUTO_GENIC = "jonas.service.ejb.auto-genic";

    /**
     * Arguments for the Auto-GenIC
    */
    public static final String AUTO_GENIC_ARGS = "jonas.service.ejb.auto-genic.args";

    // ----------------------------------------------
    // WorkManager thread pool sizes
    // ----------------------------------------------
    /** Min Work Thread property name */
    public static final String MINWORKTHREADS = "jonas.service.ejb.minworkthreads";

    /** Min Work Thread default value */
    private static final int DEFAULT_MINWORKTHREADS = 3;

    /** Max Work Thread property name */
    public static final String MAXWORKTHREADS = "jonas.service.ejb.maxworkthreads";

    /** Max Work Thread default value */
    private static final int DEFAULT_MAXWORKTHREADS = 80;

    /** Thread Wait Timeout property name */
    public static final String THREADWAITTIMEOUT = "jonas.service.ejb.threadwaittimeout";

    /** Thread Wait Timeout default value */
    private static final int DEFAULT_THREADWAITTIMEOUT = 60;

    /** The WorkManager instance */
    private JWorkManager workManager;

    /**
     * The name of the JONAS_BASE directory.
     */
    protected static final String JONAS_BASE = JProp.getJonasBase();

    /**
     * The name of the directory containing the ejb-jar files.
     */
    protected static final String EJBJARS_DIR = JONAS_BASE + File.separator + "ejbjars";

    /**
     * List of autoloaded directories.
     */
    private ArrayList autoloadDirectories = new ArrayList();

    /** base for properties */
    public static final String JONAS_EJB = "jonas.service.ejb";

    /** autoloaddir property */
    public static final String AUTOLOADDIR = JONAS_EJB + ".autoloaddir";

    /** parsing with validation property */
    public static final String PARSINGWITHVALIDATION = JONAS_EJB + ".parsingwithvalidation";

    /** descriptors property */
    public static final String DESCRIPTORS = JONAS_EJB + ".descriptors";

    /** EJB service class */
    public static final String CLASS = JONAS_EJB + ".class";

    /** BeanManaged Management properties */
    public static final String BMP = "Bean-Managed";

    /** ContainerManaged Management properties */
    public static final String CMP = "Container-Managed";

    /** applications parent classloader */
    private ClassLoader appsClassLoader = null;

    /**
     * The name of the working directory.
     */
    private static final String WORK_DIR = JProp.getWorkDir();

    /**
     * The name of the working ejbjars directory.
     */
    private static final String WORK_EJBJARS_DIR = WORK_DIR + File.separator + "ejbjars";

    /**
     *
     */
    private static String ejbjarsDir = null;

    /**
     * Reference on the DeployerLog which is the class that manage the accesses
     * to the log file (to remove the jar).
     */
    private DeployerLog jarDeployerLog = null;

    /**
     * Auto-GenIC has been enabled or not ?
     */
    private boolean autoGenIC = true;

    /**
     * List of arguments for the autoGenIC.
     */
    private List autoGenICArgsList = null;


    // -------------------------------------------------------------------
    // JOnAS Service Implementation
    // -------------------------------------------------------------------

    /**
     * Init the Service. Configuration information is passed thru a Context
     * object.
     */
    public void doInit(Context ctx) throws ServiceException {
        servlog = Log.getLogger(Log.JONAS_SERVER_PREFIX);
        loaderlog = Log.getLogger(Log.JONAS_LOADER_PREFIX);

        if (servlog.isLoggable(BasicLevel.DEBUG)) {
            servlog.log(BasicLevel.DEBUG, "");
        }

        // get apps ClassLoader
        try {
            LoaderManager lm = LoaderManager.getInstance();
            appsClassLoader = lm.getAppsLoader();
        } catch (Exception e) {
            servlog.log(BasicLevel.ERROR, "Cannot get the Applications ClassLoader from EJB Container Service");
            throw new ServiceException("Cannot get the Applications ClassLoader from EJB Container Service", e);
        }

        ServiceManager serviceManager = null;
        try {
            serviceManager = ServiceManager.getInstance();
        } catch (Exception e) {
            servlog.log(BasicLevel.ERROR, "Cannot initialize the EJB Container Service");
            throw new ServiceException("Cannot initialize the EJB Container Service", e);
        }
        // Get the used services
        try {
            // Get the Transaction Manager via the jtm service
            TransactionService jtmService = (TransactionService) serviceManager.getTransactionService();
            tm = jtmService.getTransactionManager();
        } catch (ServiceException e) {
            servlog.log(BasicLevel.ERROR, "Cannot initialize the EJB Container Service");
            throw new ServiceException("Cannot initialize the EJB Container Service", e);
        }

        try {
            // Get the Security service
            securityService = (SecurityService) serviceManager.getSecurityService();
        } catch (ServiceException se) {
            // The Security service may not be started
            securityService = null;
        }

        try {
            // Get the JMS manager via the JMS service
            JmsService jmsService = (JmsService) serviceManager.getJmsService();
            jms = jmsService.getJmsManager();
        } catch (ServiceException e) {
            // the JMS service may not be started
            jms = null;
        }

        try {
            // Get the JMX Server via JMX Service
            JmxService jmxService = (JmxService) serviceManager.getJmxService();
            mbeanServer = jmxService.getJmxServer();
        } catch (ServiceException e) {
            // the JMX service may not be started
            mbeanServer = null;
        }
        oRegistry = JonasMBeanTools.getRegistry();

        // Set the XML parsing mode to no validation
        String parsingMode;
        try {
            parsingMode = (String) ctx.lookup(PARSINGWITHVALIDATION);
        } catch (NamingException e) {
            // No problem if there is no value for 'parsingwithvalidation'
            // (false by default)
            parsingMode = "false";
        }
        if ("false".equalsIgnoreCase(parsingMode)) {
            EjbManagerWrapper.setParsingWithValidation(false);
            if (servlog.isLoggable(BasicLevel.DEBUG)) {
                servlog.log(BasicLevel.DEBUG, "XML parsing without validation");
            }
        } else {
            if (servlog.isLoggable(BasicLevel.DEBUG)) {
                servlog.log(BasicLevel.DEBUG, "XML parsing with validation");
            }
        }

        // Init the ejb names to be loaded when starting
        String descsValue;
        try {
            descsValue = (String) ctx.lookup(DESCRIPTORS);
        } catch (NamingException e) {
            // No problem if there is no value for 'descriptors' (no ejb to
            // load)
            descsValue = null;
        }
        if (descsValue != null) {
            StringTokenizer st = new StringTokenizer(descsValue, ",");
            while (st.hasMoreTokens()) {
                String fileName = st.nextToken().trim();
                ejbNames.add(fileName);
            }
        }

        // Add the jars of the jonas.service.ejb.autoloaddir property
        String dirValue;
        ArrayList autoDirs = new ArrayList();
        try {
            dirValue = (String) ctx.lookup(AUTOLOADDIR);
        } catch (NamingException e) {
            // No problem if there is no value for 'autoloaddir'
            //(no ejb to load)
            dirValue = null;
        }
        if (dirValue != null) {
            StringTokenizer st = new StringTokenizer(dirValue, ",");
            while (st.hasMoreTokens()) {
                String dirName = st.nextToken().trim();
                addEjbjars(dirName);
                autoDirs.add(dirName);
            }
        }
        // Build autoload directories
        File oFile;
        for (int i = 0; i < autoDirs.size(); i++) {
            try {
                oFile = new File(EJBJARS_DIR, autoDirs.get(i).toString());
                if (oFile.exists()) {
                    autoloadDirectories.add(oFile.getCanonicalPath());
                } else {
                    oFile = new File(autoDirs.get(i).toString());
                }
            } catch (Exception e) {
                String err = "Error when trying to verify Ejbjar autoload directory : " + autoDirs.get(i);
                servlog.log(BasicLevel.ERROR, err, e);
            }
        }

        // Create the WorkManager (thread Pool)
        int minWorkThreads;
        try {
            String plz = (String) ctx.lookup(MINWORKTHREADS);
            minWorkThreads = (new Integer(plz)).intValue();
        } catch (NamingException e) {
            // default value will be used.
            minWorkThreads = DEFAULT_MINWORKTHREADS;
        }
        int maxWorkThreads;
        try {
            String mxplz = (String) ctx.lookup(MAXWORKTHREADS);
            maxWorkThreads = (new Integer(mxplz)).intValue();
        } catch (NamingException e) {
            // default value will be used.
            maxWorkThreads = DEFAULT_MAXWORKTHREADS;
        }
        int threadWaitTimeout;
        try {
            String tto = (String) ctx.lookup(THREADWAITTIMEOUT);
            threadWaitTimeout = (new Integer(tto)).intValue();
        } catch (NamingException e) {
            // default value will be used.
            threadWaitTimeout = DEFAULT_THREADWAITTIMEOUT;
        }
        workManager = new JWorkManager(minWorkThreads, maxWorkThreads, tm, threadWaitTimeout);

        // Auto GenIC ?
        try {
            String propAutoGenIC = (String) ctx.lookup(AUTO_GENIC);
            if (propAutoGenIC != null) {
                autoGenIC = new Boolean(propAutoGenIC).booleanValue();
            }
            if (!autoGenIC) {
                servlog.log(BasicLevel.INFO, "Auto GenIC has been disabled");
            }
        } catch (NamingException e) {
            servlog.log(BasicLevel.DEBUG, "Property '" + AUTO_GENIC + "' is missing", e);
        }

        // Arguments for Auto-GenIC
        autoGenICArgsList = new ArrayList();
        int autoGenICArgInt = 0;
        boolean loopArg = true;
        while (loopArg) {
            try {
                String arg = (String) ctx.lookup(AUTO_GENIC_ARGS + "." + autoGenICArgInt);
                autoGenICArgsList.add(arg);
            } catch (NamingException e) {
                loopArg = false;
            }
            autoGenICArgInt++;
        }
        if (!autoGenICArgsList.isEmpty()) {
            servlog.log(BasicLevel.INFO, "Auto GenIC args are set to '" + autoGenICArgsList + "'.");
        }


        // Create and register the MBean for this WorkManager
        ObjectName mbn;
        try {
            mbn = JonasObjectName.workManager();
            ManagedBean oManaged = oRegistry.findManagedBean("WorkManager");
            ModelMBean oMBean = oManaged.createMBean(new WorkManagerMBean(workManager));
            mbeanServer.registerMBean(oMBean, mbn);
        } catch (Exception e) {
            servlog.log(BasicLevel.ERROR, "Cannot create MBean", e);
        }

        if (servlog.isLoggable(BasicLevel.DEBUG)) {
            servlog.log(BasicLevel.DEBUG, "EJB Container Service initialized");
        }
    }

    /**
     * @return Returns the WorkManager instance
     */
    public JWorkManager getWorkManager() {
        return workManager;
    }

    /**
     * Start the Service Initialization of the service is already done.
     */
    public void doStart() throws ServiceException {

        ejbjarsDir = WORK_EJBJARS_DIR + File.separator + getJonasServerName();

        URL ejbjarsUrl = null;
        try {
            ejbjarsUrl = new File(ejbjarsDir).toURL();
        } catch (MalformedURLException mue) {
            throw new ServiceException("Error when trying to get the URL of the jonasroot/apps directory", mue);
        }

        File fLog = new File(ejbjarsUrl.getFile() + File.separator + getJonasServerName() + ".log");
        if (!fLog.exists()) {
            try {
                //create
                fLog.getParentFile().mkdirs();
                fLog.createNewFile();
            } catch (IOException e) {
                throw new ServiceException("cannot create the log file" + fLog, e);
            }
        }

        //get the logger
        try {
            jarDeployerLog = new DeployerLog(fLog);
        } catch (DeployerLogException e) {
            throw new ServiceException("Can not get an EarDeployerLog", e);
        }

        // create the jar deployment work file control task
        JarCleanTask jarCleanTask = new JarCleanTask(jarDeployerLog);

        //get the cleaner ref
        workCleaner = WorkCleaner.getInstance();

        // add the jar deployment work file control task
        try {
            workCleaner.registerTask(jarCleanTask);
        } catch (CleanerException ce) {
            throw new ServiceException("Cannot register the JAR clean task", ce);

        }

        // force the cleaning immediately
        workCleaner.executeTasks();

        // Creates all predefined containers
        // 1 container per ejbjar file.
        String fileName = null;
        Context contctx = null;
        for (int i = 0; i < ejbNames.size(); i++) {
            fileName = (String) ejbNames.elementAt(i);
            try {
                contctx = new CompNamingContext(fileName);
                contctx.rebind("filename", fileName);
            } catch (NamingException ne) {
                servlog.log(BasicLevel.WARN, "Cannot create container for " + fileName
                        + " because of a NamingException : " + ne.toString());
            }

            try {
                createContainer(contctx);
            } catch (Exception e) {
                servlog.log(BasicLevel.WARN, "Cannot create container for " + fileName, e);
                // Close naming context
                try {
                    contctx.close();
                } catch (NamingException nne) {
                    if (servlog.isLoggable(BasicLevel.DEBUG)) {
                        servlog.log(BasicLevel.DEBUG, "Cannot close deploy context for " + fileName, nne);
                    }
                }
            }
        }

        // Register EJBService MBean : EJBServiceImplMBean
        if (mbeanServer != null) {
            try {
                mbeanServer.registerMBean(this, JonasObjectName.ejbService());
            } catch (MBeanException mbe) {
                // registration failed because a MBeanException
                servlog.log(BasicLevel.WARN, "Cannot register the EJB Container Service in the MBean Server.");
                servlog.log(BasicLevel.WARN, "Regstration failure cause : " + mbe.getTargetException().toString());
            } catch (OperationsException me) {
                // registration failed because a OperationsException JMX
                // Exception
                servlog.log(BasicLevel.WARN, "Cannot register the EJB Container Service in the MBean Server.", me);
            }
        }
    }

    /**
     * Stop the service: - Remove all JOnAS Containers. - Unbinds all the
     * EJBHome names from JNDI
     */
    public void doStop() {

        Enumeration lc = containers.elements();
        while (lc.hasMoreElements()) {
            Container cont = (Container) lc.nextElement();
            removeContainer(cont);
        }
        if (mbeanServer != null) {
            try {
                // unregister EJBService MBean : EJBServiceImplMBean
                mbeanServer.unregisterMBean(JonasObjectName.ejbService());
            } catch (Exception e) {
                servlog.log(BasicLevel.ERROR, "Cannot stop the EJB Container Service", e);
            }
        }

        if (servlog.isLoggable(BasicLevel.DEBUG)) {
            servlog.log(BasicLevel.DEBUG, "EJB Container Service stopped");
        }
    }

    // -------------------------------------------------------------------
    // EJBService Implementation
    // -------------------------------------------------------------------

    /**
     * Create a JOnAS Container for all the beans that are described in a .xml
     * file, or belong to .jar file.
     * @return The ObjectName of the MBean associated to the container (i.e. to
     *         the deployed module)
     * @throws Exception if an error occur during the creation of the container.
     */
    public String createContainer(Context ctx) throws Exception {
        if (servlog.isLoggable(BasicLevel.DEBUG)) {
            servlog.log(BasicLevel.DEBUG, "");
        }

        // Get the file name
        String fileName = (String) ctx.lookup("filename");
        File f = new File(fileName).getCanonicalFile();

        boolean isEjbJar = fileName.toLowerCase().endsWith(".jar");

        // check if the file exists
        // In case of the name is a ejb-jar file name, check also in
        // the JONAS_BASE/ejbjars directory
        try {
            if (!f.exists() && isEjbJar) {
                String ejbjarFileName = EJBJARS_DIR + File.separator + fileName;
                f = new File(ejbjarFileName).getCanonicalFile();
                if (!f.exists()) {
                    servlog.log(BasicLevel.ERROR, "createContainer: " + fileName + " not found");
                    throw new NamingException(fileName + " not found");
                }
            }
        } catch (IOException e) {
            String err = "Invalid ejbjar file name '" + fileName;
            servlog.log(BasicLevel.ERROR, err);
            throw new Exception(err, e);
        }
        boolean isEjbJarFile = isEjbJar && f.isFile();
        boolean isEjbJarXml = fileName.toLowerCase().endsWith(".xml") && f.isFile();

        // Force fileName with slash to avoid conflict under Windows (slash and
        // back-slash)
        try {
            fileName = f.toURL().getPath();
        } catch (MalformedURLException e) {
            servlog.log(BasicLevel.ERROR, "Invalid ejb-jar file name '" + fileName + "'", e);
        }

        if (f.isFile()) {
            if (!isEjbJar && !isEjbJarXml) {
                throw new ServiceException("The ejbjar to deploy is not a jar file nor an xml file");
            }
        }

        // Check if container already exists
        if (getContainer(fileName) != null) {
            servlog.log(BasicLevel.ERROR, "createContainer: " + fileName + " already exists");
            throw new Exception("Container already exists");
        }

        // In order to avoid lock problem after undeploy
        // the jar file is copied under the working directory before its loading
        // ! only if the ejbjars isn't included in an ear
        boolean isInEar = true;
        try {
            ctx.lookup("earClassLoader");
        } catch (NamingException ne) {
            isInEar = false;
        }

        // if need, call Genic. Only in single ejb-jar mode.
        // In ear case, this has been checked by ear service.
        if (!isInEar && isEjbJar) {
            checkGenIC(fileName, null);

            ObjectName j2eeServer = J2eeObjectName.J2EEServer(this.getDomainName(), this.getJonasServerName());
            WsGenChecker wsgc = new WsGenChecker(this.mbeanServer, fileName, j2eeServer);
            String resultFilename = wsgc.checkWsGen();

            if (resultFilename.endsWith(".ear")) {
                // WsGen has produced a new Application
                // It has already been deployed by 'ear' service
                // TODO Should return the EJB Module ObjectName of the ear
                return J2eeObjectName.getEJBModule(this.getDomainName(),
                                                   this.getJonasServerName(),
                                                   buildEJBModuleName(resultFilename),
                                                   buildEJBModuleName(fileName)).getCanonicalName();
            }
        }


        String wkFileName = null;

        if (!isInEar && isEjbJarFile) {
            // Get the destination file
            wkFileName = ejbjarsDir + File.separator + FileManager.fileToTimeStampDir(f.toURL(), ".jar");

            if (servlog.isLoggable(BasicLevel.DEBUG)) {
                servlog.log(BasicLevel.DEBUG, "filename=" + fileName);
                servlog.log(BasicLevel.DEBUG, "wkFilename=" + wkFileName);
            }

            // copy the file
            FileUtils.copyFile(fileName, wkFileName);

            // add an entry in the log file
            try {
                jarDeployerLog.addEntry(f, new File(wkFileName));
            } catch (DeployerLogException e) {
                String err = "Error while adding the " + fileName + " entry in the log file";
                servlog.log(BasicLevel.ERROR, err + " : " + e.getMessage());
                throw new Exception(err, e);
            }

            if (servlog.isLoggable(BasicLevel.DEBUG)) {
                servlog.log(BasicLevel.DEBUG, "Create wk file :" + wkFileName);
            }

        } else {
            // in this case, no needs to copy the file
            wkFileName = fileName;
            if (servlog.isLoggable(BasicLevel.DEBUG)) {
                servlog.log(BasicLevel.DEBUG, "No needs wk file :" + wkFileName);
            }
        }

        // Classloader
        URL[] url = null;
        try {
            url = new URL[1];
            url[0] = (new File(wkFileName)).toURL();
        } catch (MalformedURLException e) {
            servlog.log(BasicLevel.ERROR, "Invalid ejb-jar file name '" + wkFileName + "'", e);
        }

        // Get the classloaders
        URLClassLoader ejbClassLoader = null;
        URLClassLoader earClassLoader = null;
        try {
            earClassLoader = (URLClassLoader) ctx.lookup("earClassLoader");
            ejbClassLoader = (URLClassLoader) ctx.lookup("ejbClassLoader");
            if (loaderlog.isLoggable(BasicLevel.DEBUG)) {
                loaderlog.log(BasicLevel.DEBUG, "earClassLoader=" + earClassLoader);
            }
        } catch (NamingException ne) {
            // no ear classloader => ejb application case (not ear case).
            if (isEjbJar) {
                ejbClassLoader = new EjbJarClassLoader(url, appsClassLoader);
            } else {
                ejbClassLoader = (URLClassLoader) appsClassLoader;
            }
            if (loaderlog.isLoggable(BasicLevel.DEBUG)) {
                loaderlog.log(BasicLevel.DEBUG, "parent Loader=" + appsClassLoader);
            }
        }
        if (loaderlog.isLoggable(BasicLevel.DEBUG)) {
            loaderlog.log(BasicLevel.DEBUG, "ejbClassLoader=" + ejbClassLoader);
        }

        // Get the deployment descriptor from file
        DeploymentDesc dd = null;
        try {
            // ejb-jar file
            dd = EjbManagerWrapper.getDeploymentDesc(url[0], ejbClassLoader, earClassLoader);
        } catch (DeploymentDescException e) {
            String err = "Cannot read the deployment descriptors '" + fileName + "'";
            servlog.log(BasicLevel.ERROR, err);
            servlog.log(BasicLevel.ERROR, "DeploymentDescException:" + e);
            throw new ServiceException(err, e);
        }

        // container name
        String cname = buildEJBModuleName(fileName);
        if (cname == null) {
            //use displayName
            cname = "EJB container ";
            if (dd.getDisplayName() != null) {
                cname += dd.getDisplayName();
            }
        }

        // Create the Container Object
        JContainer cont = new JContainer(cname, fileName, wkFileName, ejbClassLoader);
        cont.setContainerNaming(NamingManager.getInstance());
        cont.setTransactionManager(tm);
        cont.setPrincipalFactory(new PrincipalFactoryImpl());
        cont.setJmsManager(jms);

        // Set the name of the ear application containing this container
        // in the case of an ear application.
        URL earUrl = null;
        String earFileName;
        try {
            earUrl = (URL) ctx.lookup("earURL");
            //earFileName = (String) ctx.lookup("earFileName");
            earFileName = earUrl.getFile();
            cont.setEarFileName(earFileName);
        } catch (NamingException ne) {
            earFileName = null;
        }

        // unset the security if no security service
        if (securityService == null) {
            if (servlog.isLoggable(BasicLevel.DEBUG)) {
                servlog.log(BasicLevel.DEBUG, "EJB Container Service: working without Security Service");
            }
            cont.setSecurity(false);
        } else {
            PermissionManager permissionManager = null;
            try {
                permissionManager = new PermissionManager(dd, cont.getContextId());
                permissionManager.translateEjbDeploymentDescriptor();
                // if not in ear case, commit the policy configuration, else it
                // is done
                // by EAR service after linking all policy configuration objects
                if (!isInEar) {
                    permissionManager.commit();
                }
            } catch (Exception e) {
                servlog.log(BasicLevel.ERROR, "Can't build permission manager object for the ejbjar '" + fileName + "'", e);
            }
            cont.setPermissionManager(permissionManager);
        }

        // Give access to the WorkManager
        cont.setWorkManager(workManager);

        String domainName = getDomainName();
        String serverName = getJonasServerName();
        String j2eeAppName = EarServiceImpl.buildJ2eeApplicationName(earFileName);
        EJBModule ejbModuleMBean = null;
        ObjectName onEjbModule = null;

        // MBeans to register if the deployment is successful
        Map mbeansToRegister = new HashMap();

        // EJBModule MBean registering
        if (mbeanServer != null) {
            // Use the conatiner name (the 'name' constructor parameter of
            // JContainer class)
            // as the "name" keyProperty for the J2ee ObjectName construction
            onEjbModule = J2eeObjectName.getEJBModule(domainName, serverName, j2eeAppName, cname);

            ejbModuleMBean = new EJBModule(onEjbModule, cont, fileName, cname, j2eeAppName, earUrl);
            ejbModuleMBean.setDeploymentDescriptor(dd.getXmlContent());
            ejbModuleMBean.setJonasDeploymentDescriptor(dd.getJOnASXmlContent());

            // Use Jakarta Common Modeler API
            ManagedBean oManaged = oRegistry.findManagedBean("EJBModule");
            ModelMBean oMBean = oManaged.createMBean(ejbModuleMBean);
            mbeansToRegister.put(oMBean, onEjbModule);
        }

        // Process each bean from the DD
        BeanDesc[] beans = dd.getBeanDesc();
        for (int i = 0; i < beans.length; i++) {
            BeanFactory bf = cont.addBean(beans[i]);
            beans[i].setBeanFactory(bf);
            if (mbeanServer != null) {
                try {
                    // register MBean for this ejb
                    String beanName = beans[i].getEjbName();
                    if ((beans[i] instanceof EntityBmpDesc) || (beans[i] instanceof EntityCmpDesc)) {
                        JEntityFactory jef = (JEntityFactory) bf;
                        ObjectName onEntityBean = J2eeObjectName.getEntityBean(domainName, cname, serverName,
                                j2eeAppName, beanName);
                        EntityBean entityBeanMBean; // new EntityBean
                        // declaration
                        if (beans[i] instanceof EntityBmpDesc) {
                            entityBeanMBean = new EntityBean(onEntityBean.toString(), jef, BMP);
                        } else {
                            entityBeanMBean = new EntityBean(onEntityBean.toString(), jef, CMP);
                        }
                        ManagedBean oManaged = oRegistry.findManagedBean("EntityBean");
                        ModelMBean oMBean = oManaged.createMBean(entityBeanMBean);
                        mbeansToRegister.put(oMBean, onEntityBean);
                        // Update the EJBModule
                        ejbModuleMBean.addEjb(onEntityBean.toString());
                    } else if (beans[i] instanceof SessionStatefulDesc) {
                        JSessionFactory jsf = (JSessionFactory) bf;
                        ObjectName onStatefulSessionBean = J2eeObjectName.getStatefulSessionBean(domainName, cname,
                                serverName, j2eeAppName, beanName);
                        StatefulSessionBean sessionBeanMBean = new StatefulSessionBean(
                                onStatefulSessionBean.toString(), jsf);
                        ManagedBean oManaged = oRegistry.findManagedBean("StatefulSessionBean");
                        ModelMBean oMBean = oManaged.createMBean(sessionBeanMBean);
                        mbeansToRegister.put(oMBean, onStatefulSessionBean);
                        // Update the EJBModule
                        ejbModuleMBean.addEjb(onStatefulSessionBean.toString());
                    } else if (beans[i] instanceof SessionStatelessDesc) {
                        JStatelessFactory jsf = (JStatelessFactory) bf;
                        ObjectName onStatelessSessionBean = J2eeObjectName.getStatelessSessionBean(domainName, cname,
                                serverName, j2eeAppName, beanName);
                        StatelessSessionBean sessionBeanMBean = new StatelessSessionBean(onStatelessSessionBean
                                .toString(), jsf);
                        ManagedBean oManaged = oRegistry.findManagedBean("StatelessSessionBean");
                        ModelMBean oMBean = oManaged.createMBean(sessionBeanMBean);
                        mbeansToRegister.put(oMBean, onStatelessSessionBean);
                        // Update the EJBModule
                        ejbModuleMBean.addEjb(onStatelessSessionBean.toString());
                    } else {
                        JFactory jmf = (JFactory) bf;
                        ObjectName onMessageDrivenBean = J2eeObjectName.getMessageDrivenBean(domainName, cname,
                                serverName, j2eeAppName, beanName);
                        MessageDrivenBean messageDrivenBeanMBean = new MessageDrivenBean(onMessageDrivenBean.toString(), jmf);
                        ManagedBean oManaged = oRegistry.findManagedBean("MessageDrivenBean");
                        ModelMBean oMBean = oManaged.createMBean(messageDrivenBeanMBean);
                        mbeansToRegister.put(oMBean, onMessageDrivenBean);
                        // Update the EJBModule
                        ejbModuleMBean.addEjb(onMessageDrivenBean.toString());
                    }
                } catch (Exception e) {
                    servlog.log(BasicLevel.ERROR, "Cannot create container", e);
                }
            }
        }
        // Init now pools of instances, in case min-pool-size have been defined.
        // This must be done once all beans have been deployed.
        for (int i = 0; i < beans.length; i++) {
            BeanFactory bf = beans[i].getBeanFactory();
            bf.initInstancePool();
        }

        // Deployment was successful, add the bean as deployed and create the
        // MBeans
        containers.addElement(cont);
        for (Iterator it = mbeansToRegister.keySet().iterator(); it.hasNext();) {
            ModelMBean mbean = (ModelMBean) it.next();
            ObjectName oName = (ObjectName) mbeansToRegister.get(mbean);
            mbeanServer.registerMBean(mbean, oName);

        }

        // Restart Timers. Must be done when containers has been updated.
        for (int i = 0; i < beans.length; i++) {
            BeanFactory bf = beans[i].getBeanFactory();
            bf.restartTimers();
        }

        if (servlog.isLoggable(BasicLevel.DEBUG)) {
            servlog.log(BasicLevel.DEBUG, "created container for " + fileName);
        }
        //return cont;
        return onEjbModule.toString();
    }

    /**
     * Get the Container by its file name (.xml or .jar)
     * @param fileName given file name on which
     * @return Container
     */
    public Container getContainer(String fileName) {
        if (servlog.isLoggable(BasicLevel.DEBUG)) {
            servlog.log(BasicLevel.DEBUG, fileName);
        }

        // In case of the name is a ejb-jar file name, check also in
        // the JONAS_BASE/ejbjars directory and JONAS_BASE/ejbjars/autoload

        String ejbjarFileName = EJBJARS_DIR + File.separator + fileName;
        File f = null;
        String pathEjbJarDirFile;
        String pathFile = "";
        try {
            f = new File(ejbjarFileName).getCanonicalFile();
            // Force pathEjbJarDirFile with slash to avoid conflict under
            // Windows (slash and back-slash)
            pathEjbJarDirFile = f.toURL().getPath();
        } catch (Exception e) {
            // If filename was an absolute path, it can throw an exception
            // The pathFile will be empty
            pathEjbJarDirFile = "";
        }
        try {
            f = new File(fileName).getCanonicalFile();
            // Force pathFile with slash to avoid conflict under Windows (slash
            // and back-slash)
            pathFile = f.toURL().getPath();
        } catch (Exception e) {
            String err = "Error while trying to get canonical file '" + fileName + "'";
            servlog.log(BasicLevel.ERROR, err);
            return null;
        }

        // for each container loaded
        Enumeration lc = containers.elements();
        while (lc.hasMoreElements()) {
            Container cont = (JContainer) lc.nextElement();
            String contName = cont.getExternalFileName();

            // first, search abs path
            if (contName.equals(pathFile)) {
                return cont;
            } else if (contName.equals(pathEjbJarDirFile)) {
                // next, search in ejbjar path
                return cont;
            } else {
                // else search in autoloadir
                List lAlDirs = getAutoloadDirectories();
                for (int i = 0; i < lAlDirs.size(); i++) {
                    String alDir = (String) lAlDirs.get(i);

                    String alFileName = alDir + File.separator + fileName;
                    try {
                        f = new File(alFileName).getCanonicalFile();
                        // Force alFileName with slash to avoid conflict under
                        // Windows (slash and back-slash)
                        alFileName = f.toURL().getPath();
                    } catch (Exception e) {
                        // If filename was an absolute path, it can throw an
                        // exception
                        alFileName = alDir + File.separator + fileName;
                    }
                    if (servlog.isLoggable(BasicLevel.DEBUG)) {
                        servlog.log(BasicLevel.DEBUG, "alFileName= " + alFileName);
                    }

                    if (contName.equals(alFileName)) {
                        return cont;
                    }
                }
            }
        }

        // not found
        return null;
    }

    /**
     * Remove a JOnAS container
     */
    public void removeContainer(Container cont) {
        if (servlog.isLoggable(BasicLevel.DEBUG)) {
            servlog.log(BasicLevel.DEBUG, cont.getName());
        }

        if (mbeanServer != null) {
            String moduleName = cont.getName();
            String earFileName = cont.getEarFileName();
            String earName = null;
            if (earFileName != null) {
                earName = EarServiceImpl.buildJ2eeApplicationName(earFileName);
            }
            String domainName = getDomainName();
            String serverName = getJonasServerName();
            ObjectName onEjbModule = J2eeObjectName.getEJBModule(domainName, serverName, earName, moduleName);

            // try to remove MBeans corresponding to the EJBs in the module
            ObjectName onEjb = null;
            try {
                String[] onEjbs = (String[]) mbeanServer.getAttribute(onEjbModule, "ejbs");
                for (int i = 0; i < onEjbs.length; i++) {
                    onEjb = new ObjectName(onEjbs[i]);
                    mbeanServer.unregisterMBean(onEjb);
                }
            } catch (Exception e) {
                servlog.log(BasicLevel.ERROR, "Problem when trying to unregsiter MBean " + onEjb.toString(), e);
            }

            // try to remove MBean corresponding to the module
            try {
                mbeanServer.unregisterMBean(onEjbModule);
                if (servlog.isLoggable(BasicLevel.DEBUG)) {
                    servlog.log(BasicLevel.DEBUG, "unregistered MBean: " + onEjbModule.toString());
                }
            } catch (Exception e) {
                servlog.log(BasicLevel.ERROR, "Problem when trying to unregsiter MBean " + onEjbModule.toString(), e);
            }
        }
        cont.remove();
        containers.removeElement(cont);

        // Run the garbage collector
        Runtime.getRuntime().gc();

    }

    /**
     * List JOnAS container created by EJB Service
     */
    public Container[] listContainers() {
        if (servlog.isLoggable(BasicLevel.DEBUG)) {
            servlog.log(BasicLevel.DEBUG, "size= " + containers.size());
        }

        Container[] ret = new Container[containers.size()];
        containers.copyInto(ret);
        return ret;
    }

    /**
     * Synchronized all entity bean containers
     * @param passivate passivate instances after synchronization.
     */
    public void syncAllEntities(boolean passivate) {
        if (servlog.isLoggable(BasicLevel.DEBUG)) {
            servlog.log(BasicLevel.DEBUG, "");
        }

        Enumeration lc = containers.elements();
        while (lc.hasMoreElements()) {
            JContainer cont = (JContainer) lc.nextElement();
            cont.syncAll(true, passivate);
        }
    }

    /** ******* MBEan methods ******** */

    /**
     * MBean method
     * @return Integer Total Number of Container currently in JOnAS
     */
    public Integer getCurrentNumberOfContainer() {
        return new Integer(containers.size());
    }

    /**
     * MBean method
     * @return Integer Total Number of Bean Type currently in JOnAS
     */
    public Integer getTotalCurrentNumberOfBeanType() {
        int count = 0;
        Enumeration lc = containers.elements();
        while (lc.hasMoreElements()) {
            JContainer cont = (JContainer) lc.nextElement();
            count += cont.getBeanNb();
        }
        return new Integer(count);
    }

    /**
     * MBean method
     * @return Integer Total Number of Bmp Type currently in JOnAS
     */
    public Integer getTotalCurrentNumberOfBMPType() {
        int count = 0;
        Enumeration lc = containers.elements();
        while (lc.hasMoreElements()) {
            JContainer cont = (JContainer) lc.nextElement();
            count += cont.getEntityBMPNb();
        }
        return new Integer(count);
    }

    /**
     * MBean method
     * @return Integer Total Number of Cmp Type currently in JOnAS
     */
    public Integer getTotalCurrentNumberOfCMPType() {
        int count = 0;
        Enumeration lc = containers.elements();
        while (lc.hasMoreElements()) {
            JContainer cont = (JContainer) lc.nextElement();
            count += cont.getEntityCMPNb();
        }
        return new Integer(count);
    }

    /**
     * MBean method
     * @return Integer Total Number of Sbf Type currently in JOnAS
     */
    public Integer getTotalCurrentNumberOfSBFType() {
        int count = 0;
        Enumeration lc = containers.elements();
        while (lc.hasMoreElements()) {
            JContainer cont = (JContainer) lc.nextElement();
            count += cont.getStatefulSessionNb();
        }
        return new Integer(count);
    }

    /**
     * MBean method
     * @return Integer Total Number of Sbl Type currently in JOnAS
     */
    public Integer getTotalCurrentNumberOfSBLType() {
        int count = 0;
        Enumeration lc = containers.elements();
        while (lc.hasMoreElements()) {
            JContainer cont = (JContainer) lc.nextElement();
            count += cont.getStatelessSessionNb();
        }
        return new Integer(count);
    }

    /**
     * MBean method
     * @return Integer Total Number of Mdb Type currently in JOnAS
     */
    public Integer getTotalCurrentNumberOfMDBType() {
        int count = 0;
        Enumeration lc = containers.elements();
        while (lc.hasMoreElements()) {
            JContainer cont = (JContainer) lc.nextElement();
            count += cont.getMessageDrivenNb();
        }
        return new Integer(count);
    }

    public void removeContainer(String fileName) throws Exception {
        removeContainerMBean(fileName);
    }

    /**
     * MBean method remove a container application
     * @param fileName the Name of this container
     */
    public void removeContainerMBean(String fileName) throws Exception {
        Container cont = null;
        try {
            cont = getContainer(fileName);
        } catch (Exception e) {
            String err = "Error while trying to find file '" + fileName + "'";
            servlog.log(BasicLevel.ERROR, err);
            throw new Exception(err, e);
        }

        if (cont != null) {
            removeContainer(cont, false);
        } else {
            String err = "Cannot remove the non-existant container '" + fileName + "'";
            servlog.log(BasicLevel.ERROR, err);
            throw new Exception(err);
        }
    }

    public String createContainer(String file) throws Exception {
        return createContainerMBean(file);
    }

    /**
     * MBean method allowing to create an EJB container for the EJBs contained
     * in a .jar (or .xml) file.
     * @param file Name of the file to be deployed
     * @return The ObjectName of the MBean associated to the container (to the
     *         deployed module)
     */
    public String createContainerMBean(String file) throws Exception {
        String ejbModuleObjectName = null;
        try {
            Context contctx = new CompNamingContext(file);
            contctx.rebind("filename", file);
            ejbModuleObjectName = createContainer(contctx);
        } catch (Exception e) {
            throw new Exception("Cannot create Container", e);
        }
        return ejbModuleObjectName;
    }

    /**
     * Test if the specified file is already deployed (if a container is created
     * for this jar).
     * @param fileName the name of the jar file
     * @return true if the jar was deployed, false otherwise
     */
    public Boolean isJarDeployed(String fileName) {
        return new Boolean(isJarLoaded(fileName));
    }

    /**
     * Test if the specified jar identified with its work name is already
     * deployed (if a container is created for this jar).
     * @param workFileName the internal name of the jar file (working copy)
     * @return true if the jar was deployed, false otherwise
     */
    public boolean isJarDeployedByWorkName(String workFileName) {
        if (servlog.isLoggable(BasicLevel.DEBUG)) {
            servlog.log(BasicLevel.DEBUG, workFileName);
        }

        // for each container loaded
        Enumeration lc = containers.elements();
        while (lc.hasMoreElements()) {
            Container cont = (JContainer) lc.nextElement();

            // get name of the container (basename)
            // cont.getFileName return the internal name (working copy)
            String contName = new File(cont.getFileName()).getName();
            if (contName.equals(workFileName)) {
                return true;
            }
        }

        // not found
        return false;
    }

    // Kind of resources:
    /** DataSource Resource Type */
    static final int DATASOURCE = 1;

    /** JMS Destination Resource Type */
    static final int JMS_DESTINATION = 2;

    /** JMS Factory Resource Type */
    static final int JMS_FACTORY = 3;

    /** Mail Factory Resource Type */
    static final int MAIL_FACTORY = 4;

    /**
     * This method is used by the getXXXDependence MBean methods. It gathers the
     * ObjectNames of the EJBs using a given resource.
     * @param name the resource name
     * @type the resource type
     * @return the ObjectNames of all the ejb using this resource
     */
    Set getDependence(String name, int type) {
        Set resultObjectName = new HashSet(); // to be returned by this method
        Set resultProperties = new HashSet(); // constructed by the JContainer
        Enumeration lc = containers.elements();
        try {
            Context ctx = new InitialContext();
            ctx.lookup(name);
            ctx.close();
            // this resource is rebind
            while (lc.hasMoreElements()) {
                JContainer cont = (JContainer) lc.nextElement();
                Set depProps = null; // Dependences described as a set of
                // Properties objects
                switch (type) {

                case DATASOURCE:
                    depProps = cont.getDataSourceDependence(name);
                    break;

                case JMS_DESTINATION:
                    depProps = cont.getJmsDestinationDependence(name);
                    break;

                case JMS_FACTORY:
                    depProps = cont.getJmsConnectionFactoryDependence(name);
                    break;

                case MAIL_FACTORY:
                    depProps = cont.getMailFactoryDependence(name);
                    break;

                }
                resultProperties.addAll(depProps);
                // change string to ObjectName
                try {
                    resultObjectName = convertToObjectNames(resultProperties);
                } catch (Exception e) {
                    servlog.log(BasicLevel.ERROR, "EjbServiceImpl: Object Name Error", e);
                }
            }
        } catch (NamingException ne) {
            // unexisting ds
            resultObjectName = new HashSet();
        }
        return resultObjectName;
    }

    /**
     * Converts a Set of properties to ObjectNames
     * @param resultProperties properties to be converted
     * @return Returns a Set of ObjectNames
     */
    private Set convertToObjectNames(Set resultProperties) {
        Set resultObjectName = new HashSet();
        Iterator it = resultProperties.iterator();
        String domainName = getDomainName();
        String serverName = getJonasServerName();
        Properties item = null; // a item in resultProperties
        String ejbType = null;
        String ejbName = null;
        String earFileName = null;
        String moduleName = null;
        String j2eeAppName = null;
        while (it.hasNext()) {
            item = (Properties) it.next();
            ObjectName ejbObjectName = null;
            // See JContainer.beansDependence() method for info about the
            // Properties object containt
            ejbType = item.getProperty("type");
            ejbName = item.getProperty("name");
            earFileName = item.getProperty("earFileName");
            //moduleName = buildEJBModuleName(fileName);
            moduleName = item.getProperty("cname");
            if (earFileName != null) {
                j2eeAppName = EarServiceImpl.buildJ2eeApplicationName(earFileName);
            }

            if (ejbType.equals("ejbbmp") || ejbType.equals("ejbcmp")) {
                ejbObjectName = J2eeObjectName.getEntityBean(domainName, moduleName, serverName, j2eeAppName, ejbName);
            } else if (ejbType.equals("ejbsbf")) {
                ejbObjectName = J2eeObjectName.getStatefulSessionBean(domainName, moduleName, serverName, j2eeAppName,
                        ejbName);
            } else if (ejbType.equals("ejbsbl")) {
                ejbObjectName = J2eeObjectName.getStatelessSessionBean(domainName, moduleName, serverName, j2eeAppName,
                        ejbName);
            } else if (ejbType.equals("ejbmdb")) {
                ejbObjectName = J2eeObjectName.getMessageDrivenBean(domainName, moduleName, serverName, j2eeAppName,
                        ejbName);
            }
            if (ejbObjectName != null) {
                resultObjectName.add(ejbObjectName);
            }
        }
        return resultObjectName;
    }

    /**
     * MBean method
     * @return the ObjectName of all the ejbs using this datasource.
     */
    public Set getDataSourceDependence(String dsName) {
        return getDependence(dsName, DATASOURCE);
    }

    /**
     * Manangement method
     * @return the ObjectName of all the ejb using this destination.
     */
    public Set getJmsDestinationDependence(String destName) {
        return getDependence(destName, JMS_DESTINATION);
    }

    /**
     * Management method
     * @return the ObjectName of all the ejb using this Connection Factory.
     */
    public Set getJmsConnectionFactoryDependence(String cfName) {
        return getDependence(cfName, JMS_FACTORY);
    }

    /**
     * Management method
     * @return the ObjectName of all the ejb using a given Mail Factory.
     */
    public Set getMailFactoryDependence(String mfName) {
        return getDependence(mfName, MAIL_FACTORY);
    }

    /**
     * Return the list of installed EJB containers. The JAR files or the
     * directories with expanded EJB container are searched in
     * JONAS_BASE/ejbjars and all ejbjar directories 'autoload'.
     * @return The list of JAR files or the directories with expanded EJB
     *         container found
     * @throws Exception if the list can't be retrieved
     */
    public List getInstalledJars() throws Exception {
        // get JAR files found in JONAS_BASE/ejbjars
        ArrayList al = JModule.getInstalledContainersInDir(EJBJARS_DIR, JModule.EJBJAR_EXTENSION,
                JModule.EJBJAR_CHILD_DIR, JModule.EJBJAR_CONFIRM_FILE);
        // get JAR files found in all autoload directories
        for (int i = 0; i < autoloadDirectories.size(); i++) {
            al.addAll(JModule.getInstalledContainersInDir(autoloadDirectories.get(i).toString(),
                    JModule.EJBJAR_EXTENSION, JModule.EJBJAR_CHILD_DIR, JModule.EJBJAR_CONFIRM_FILE));
        }
        return al;
    }

    /**
     * Deploy the given ejb-jars of an ear file with the specified parent
     * classloader (ear classloader). (This method is only used for the ear
     * applications, not for the ejb-jar applications).
     * @param ctx the context containing the configuration to deploy the
     *        ejbjars. <BR>This context contains the following parameters :
     *        <BR>- earRootUrl the root of the ear application. <BR>- earURL
     *        filename of the EAR. <BR>- earClassLoader the ear classLoader of
     *        the ear application. <BR>- ejbClassLoader the ejb classLoader for
     *        the ejbjars. <BR>- jarURLs the list of the urls of the ejb-jars
     *        to deploy. <BR>- roleNames the role names of the security-role.
     *        <BR>
     * @throws ServiceException if an error occurs during the deployment.
     */
    public void deployJars(Context ctx) throws ServiceException {

        // Get the 6 parameters from the context
        // - earRootUrl the root of the ear application.
        // - earClassLoader the ear classLoader of the ear application.
        // - ejbClassLoader the ejb classLoader for the ejbjars.
        // - jarURLs the list of the urls of the ejb-jars to deploy.
        // - roleNames the role names of the security-role
        URL earRootUrl = null;
        URL earUrl = null;
        ClassLoader earClassLoader = null;
        ClassLoader ejbClassLoader = null;
        URL[] jarURLs = null;
        String[] roleNames = null;
        try {
            earRootUrl = (URL) ctx.lookup("earRootUrl");
            earUrl = (URL) ctx.lookup("earUrl");
            earClassLoader = (ClassLoader) ctx.lookup("earClassLoader");
            ejbClassLoader = (ClassLoader) ctx.lookup("ejbClassLoader");
            jarURLs = (URL[]) ctx.lookup("jarURLs");
            roleNames = (String[]) ctx.lookup("roleNames");
        } catch (NamingException e) {
            String err = "Error while getting parameter from context param :" + e.getMessage();
            servlog.log(BasicLevel.ERROR, err);
            throw new ServiceException(err, e);
        }

        // Deploy all the ejb-jars of the ear application.
        for (int i = 0; i < jarURLs.length; i++) {

            // Get the name of an ejb-jar to deploy.
            String fileName = jarURLs[i].getFile();
            if (servlog.isLoggable(BasicLevel.DEBUG)) {
                servlog.log(BasicLevel.DEBUG, "Deploy '" + fileName + "' for the ear service.");
            }

            // The context to give for the creation of the container
            // associated to the ejb-jar.
            Context contctx = null;
            try {
                contctx = new CompNamingContext(fileName);
                contctx.rebind("filename", fileName);
                contctx.rebind("earClassLoader", earClassLoader);
                contctx.rebind("ejbClassLoader", ejbClassLoader);
                contctx.rebind("earRoot", earRootUrl.getFile());
                //contctx.rebind("earFileName", earUrl.getFile());
                contctx.rebind("earURL", earUrl);
                contctx.rebind("roleNames", roleNames);
                createContainer(contctx);
            } catch (Exception e) {
                // An ejb-jar is corrupted so undeploy all the deployed jar
                // of the ear application.
                servlog.log(BasicLevel.ERROR, "Error when deploying '" + fileName + "'", e);
                servlog.log(BasicLevel.ERROR, "Undeploy ejb-jar of the ear application");

                for (int j = 0; j <= i; j++) {
                    Container cont = getContainer(jarURLs[j].getFile());
                    if (cont != null) {
                        removeContainer(cont, true);
                    } else {
                        servlog.log(BasicLevel.ERROR, "Cannot remove the non-existant container '" + fileName + "'");
                    }
                }
                throw new ServiceException("Error during the deployment", e);
            }
        }
        //return ejbClassLoader;
    }

    /**
     * Undeploy the given ejb-jars of an ear file. (This method is only used for
     * the ear applications, not for the ejb-jar applications).
     * @param urls the list of the urls of the ejb-jars to undeploy.
     */
    public void unDeployJars(URL[] urls) {
        for (int i = 0; i < urls.length; i++) {
            String fileName = urls[i].getFile();
            Container cont = getContainer(urls[i].getFile());
            if (cont != null) {
                removeContainer(cont, true);
            } else {
                servlog.log(BasicLevel.ERROR, "Cannot remove the non-existant container '" + fileName + "'");
            }
        }
    }

    /**
     * Remove the specified container.
     * @param cont the container to remove.
     * @param isEarCase true if only if the removeContainer method is called in
     *        the ear case, false otherwise.
     */
    public void removeContainer(Container cont, boolean isEarCase) {
        if (isEarCase == (cont.getEarFileName() != null)) {
            removeContainer(cont);
        } else {
            String err = "Cannot remove container '" + cont.getName()
                    + "' it is in an ear application. You must undeploy the ear associated.";
            servlog.log(BasicLevel.ERROR, err);
        }
    }

    /**
     * Make a cleanup of the cache of deployment descriptor. This method must be
     * invoked after the ear deployment by the EAR service. the deployment of an
     * ear by .
     * @param earClassLoader the ClassLoader of the ear application to remove
     *        from the cache.
     */
    public void removeCache(ClassLoader earClassLoader) {
        EjbManagerWrapper.removeCache(earClassLoader);
    }

    /**
     * Add recursively the ejbjars of the specified directory. If the dir has a
     * relative path, this path is relative from where the Application Server is
     * launched. If the dir is not found it will be searched in
     * $JONAS_BASE/ejbjars/ directory.
     * @param dirPath the path to the directory containing the ejbjars to load.
     */
    private void addEjbjars(String dirPath) {
        boolean found = false;

        // Look the directory relative to the $JONAS_BASE/ejbjars directory
        File dir = new File(EJBJARS_DIR + File.separator + dirPath);
        found = dir.isDirectory();

        if (found) {
            addEjbjarsFrom(dir);
        } else {
            String err = "Warning: Cannot load dir: '" + dirPath + "' ";
            err += "is not a directory or directory doesn't exist";
            servlog.log(BasicLevel.WARN, err);
        }
    }

    /**
     * Add the ejbjars of the specified directory.
     * @param dir the directory from which the ejbjars are loaded.
     * @throws ServiceException if the argument is not a directory
     */
    private void addEjbjarsFrom(File dir) throws ServiceException {

        try {
            if (dir.isDirectory()) {
                File[] files = dir.listFiles();
                for (int i = 0; i < files.length; i++) {
                    if (files[i].getPath().toLowerCase().endsWith(".jar")) {
                        ejbNames.add(files[i].getCanonicalPath());
                    } else if (files[i].isDirectory()) {
                        addEjbjarsFrom(files[i]);
                    }
                }
            } else {
                String err = "Cannot load dir: '" + dir.getPath();
                err += "' is not a directory";
                servlog.log(BasicLevel.ERROR, err);
                throw new ServiceException(err);
            }
        } catch (IOException e) {
            String err = "Invalid file name '" + dir.getPath();
            servlog.log(BasicLevel.ERROR, err);
            throw new ServiceException(err, e);
        }
    }

    /**
     * Test if the specified filename is already deployed or not
     * @param fileName the name of the jar file.
     * @return true if the jar is deployed, else false.
     */
    public boolean isJarLoaded(String fileName) {
        return (getContainer(fileName) != null);
    }

    /**
     * Return the list of all loaded EJB container.
     * @return The list of deployed EJB container
     */
    public List getDeployedJars() {
        ArrayList al = new ArrayList();
        Container oContainer;
        for (Enumeration jars = containers.elements(); jars.hasMoreElements();) {
            oContainer = (Container) jars.nextElement();
            try {
                al.add((new File(oContainer.getExternalFileName())).toURL().getPath());
            } catch (Exception e) {
                // none
            }
        }
        return al;
    }

    /**
     * Return the list of installed EJB container ready to deploy.
     * @return The list of deployable EJB container
     * @throws Exception when cannot get installed jars list
     */
    public List getDeployableJars() throws Exception {
        List al = getInstalledJars();
        al.removeAll(getDeployedJars());
        return al;
    }

    /**
     * Return the list of "autoload" directories for EJB containers.
     * @return The list of all "autoload" directories
     */
    public List getAutoloadDirectories() {
        ArrayList al = new ArrayList();
        for (int i = 0; i < autoloadDirectories.size(); i++) {
            try {
                al.add((new File(autoloadDirectories.get(i).toString())).toURL().getPath());
            } catch (Exception e) {
                // none
            }
        }
        return al;
    }

    /**
     * Return the Ejbjars directory.
     * @return The Ejbjars directory
     */
    public String getEjbjarsDirectory() {
        String sRet;
        try {
            sRet = (new File(EJBJARS_DIR)).toURL().getPath();
        } catch (Exception e) {
            sRet = null;
        }
        return sRet;
    }

    /**
     * Build the EJBModule name.
     * @param pUrl The URL of Jar file
     * @return The EJBModule name
     */
    public static String buildEJBModuleName(URL pUrl) {
        String sName;
        try {
            sName = new File(pUrl.getFile()).getName();
            if ("file".equals(pUrl.getProtocol())) {
                sName = buildEJBModuleName(sName);
            }
        } catch (NullPointerException e) {
            sName = null;
        }
        return sName;
    }

    /**
     * Build the EJBModule name.
     * @param pFilename The name of Jar file
     * @return The EJBModule name
     */
    public static String buildEJBModuleName(String pFilename) {
        String sName;
        try {
            sName = new File(pFilename).getName();
            int iPos = sName.lastIndexOf('.');
            if (iPos > -1) {
                sName = sName.substring(0, iPos);
            }
        } catch (NullPointerException e) {
            sName = null;
        }
        return sName;
    }

    /**
     * Check if current carol protocol choose is include into those choose during generation
     * @param listProtocolGenerated (string with comma separator)
     * @param listCurrentProtocol (string with comma separator)
     * @return true if we have find at least one current protocol not in those generated
     */
    private static boolean checkCurrentProtocolIncludeIntoGenerated(String listProtocolGenerated, String listCurrentProtocol) {
        Protocols generated = new Protocols(listProtocolGenerated);
        Protocols current = new Protocols(listCurrentProtocol);
        return generated.isSupported(current);
    }

    /**
     * get the attibut value in manifest file
     * @param jarName : the jar file containg manifest
     * @param attributName : the attribut name
     * @return the value corresponding to the attributName if it's exist null otherwise
     */
    private static String getAttributeInManifest(String jarName, String attributName) {
        try {
            File jarFile = new File(jarName);
            JarFile jar = new JarFile(jarFile);
            Manifest man = jar.getManifest();
            Attributes atts = man.getMainAttributes();
            return atts.getValue(attributName);
        } catch (Exception e) {
            servlog.log(BasicLevel.ERROR, "Cannot read Manifest: " + e);
            return null;
        }
    }

    /**
     * Check that GenIC have been applied on the given ejb-jar file
     * If it was not done, it run GenIC against the file.
     * @param fileName given EJB-JAR file.
     * @param urls Array of URLs used as CLASSPATH during EJB compilation
     */
    public void checkGenIC(String fileName, URL[] urls) {
        // Detect if auto genIC is disabled or not in jonas.properties file.
        if (!autoGenIC) {
            return;
        }

        // get Carol configuration to intialize Genic Arguments
        ProtocolConfiguration[] listCurrentProtocol = ConfigurationRepository.getConfigurations();

        String[] genicArgsString = null;

        // Extra args are stored in autoGenICArgsList

        int argIndex = 0;
        // append classpath
        if (urls != null && urls.length > 0) {
            // we have a CP to use
            genicArgsString = new String[4 + autoGenICArgsList.size()];

            StringBuffer classpath = new StringBuffer();
            for (int i = 0; i < urls.length; i++) {
                classpath.append(urls[i].getPath());
                if (i != (urls.length - 1)) {
                    classpath.append(File.pathSeparator);
                }
            }

            genicArgsString[argIndex++] = "-classpath";
            genicArgsString[argIndex++] = classpath.toString();
        } else {
            // no CP
            genicArgsString = new String[2 + autoGenICArgsList.size()];
        }

        genicArgsString[argIndex++] = "-protocols";
        String listProtocols = "";
        for (int i = 0; i < listCurrentProtocol.length; i++) {
            listProtocols = listProtocols + listCurrentProtocol[i].getName();
            if (i != (listCurrentProtocol.length - 1)) {
                listProtocols = listProtocols + ",";
            }
        }
        genicArgsString[argIndex++] = listProtocols;

        // Add the extra args
        Iterator itExtraArg = autoGenICArgsList.iterator();
        while (itExtraArg.hasNext()) {
            genicArgsString[argIndex++] = (String) itExtraArg.next();
        }


        String jonasVersionGenic = getAttributeInManifest(fileName, "Genic-Jonas-Version");
        String listProtocolGenerated = getAttributeInManifest(fileName, "Genic-Jonas-protocols");

        boolean isCurrentProtocolIncludeIntoGenerated = checkCurrentProtocolIncludeIntoGenerated(listProtocolGenerated, listProtocols);

        if ((jonasVersionGenic == null) || !jonasVersionGenic.equals(Version.getNumber())
                || !isCurrentProtocolIncludeIntoGenerated) {
            // Print a little explaination before calling Genic
            if (jonasVersionGenic == null) {
                servlog.log(BasicLevel.INFO, "JOnAS version was not found in the '" + fileName + "' manifest file. Auto-generating container classes...");
            } else if (!jonasVersionGenic.equals(Version.getNumber())) {
                servlog.log(BasicLevel.INFO, "JOnAS version found in the '" + fileName + "' manifest file :" + jonasVersionGenic + " is different of the current JOnAS version : " + Version.getNumber() + ". Auto-generating container classes...");
            } else if (!isCurrentProtocolIncludeIntoGenerated) {
                servlog.log(BasicLevel.INFO, "Current Carol protocol is not included in the protocols found in the '" + fileName + "' manifest file. Auto-generating container classes...");
            }

            Object[] param = {fileName, genicArgsString};
            String[] signature = {"java.lang.String", "[Ljava.lang.String;"};
            //  invoke method on the MBeanServer
            try {
                ObjectName j2eeServerON = J2eeObjectName.J2EEServer(this.getDomainName(), this.getJonasServerName());
                mbeanServer.invoke(j2eeServerON, "deployLocalFile", param, signature);

            } catch (Exception e) {
                servlog.log(BasicLevel.WARN, "createContainer: " + fileName + " can't create generated classes", e);
            }
        }
    }
}
