/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-2006 Bull S.A.S.
 * 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.web;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.Vector;

import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
import javax.naming.Context;
import javax.naming.LinkRef;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.StringRefAddr;

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.jmx.J2eeObjectName;
import org.objectweb.jonas.jmx.JmxService;
import org.objectweb.jonas.jmx.JonasObjectName;
import org.objectweb.jonas.naming.CompNamingContext;
import org.objectweb.jonas.naming.NamingManager;
import org.objectweb.jonas.server.LoaderManager;
import org.objectweb.jonas.service.AbsServiceImpl;
import org.objectweb.jonas.service.ServiceException;
import org.objectweb.jonas.service.ServiceManager;
import org.objectweb.jonas.web.lib.JarTools;
import org.objectweb.jonas.web.lib.PermissionManager;
import org.objectweb.jonas.ws.JServiceFactory;
import org.objectweb.jonas.ws.JServiceFactoryFinder;
import org.objectweb.jonas.ws.WebServicesService;
import org.objectweb.jonas_lib.deployment.api.EjbLocalRefDesc;
import org.objectweb.jonas_lib.deployment.api.EjbRefDesc;
import org.objectweb.jonas_lib.deployment.api.EnvEntryDesc;
import org.objectweb.jonas_lib.deployment.api.MessageDestinationRefDesc;
import org.objectweb.jonas_lib.deployment.api.ResourceEnvRefDesc;
import org.objectweb.jonas_lib.deployment.api.ResourceRefDesc;
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.FileManagerException;
import org.objectweb.jonas_lib.deployment.work.WarCleanTask;
import org.objectweb.jonas_lib.deployment.work.WorkCleaner;
import org.objectweb.jonas_lib.files.WsGenChecker;
import org.objectweb.jonas_lib.loader.SimpleWebappClassLoader;
import org.objectweb.jonas_lib.loader.WebappClassLoader;
import org.objectweb.jonas_lib.naming.ContainerNaming;
import org.objectweb.jonas_lib.security.PermissionManagerException;
import org.objectweb.jonas_web.deployment.api.WebContainerDeploymentDesc;
import org.objectweb.jonas_web.deployment.api.WebContainerDeploymentDescException;
import org.objectweb.jonas_web.deployment.lib.wrapper.WebManagerWrapper;
import org.objectweb.jonas_ws.deployment.api.ServiceRefDesc;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;

/**
 * This abstract class provides an implementation for a dynamic
 * JWebContainerService service.
 * @author Florent Benoit
 * @author Ludovic Bert (J2EE 1.3)
 * @author Nicolas Van Caneghem (exploded ear)
 * @author Michel-Ange Anton (contributor)
 */
public abstract class AbsJWebContainerServiceImpl extends AbsServiceImpl implements JWebContainerService,
        AbsJWebContainerServiceImplMBean {

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

    /**
     * The name of the webapps directory.
     */
    protected static final String WEBAPPS_DIR = JONAS_BASE + File.separator + "webapps";

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

    /**
     * The name of the working apps directory.
     */
    protected static final String WORK_WEBAPPS_DIR = WORK_DIR + File.separator + "webapps";

    /**
     * The name of the property used in work directory for single webapps (not ear case).
     */
    protected static final String SINGLE_WORK_WEBAPPS_DIR_SUFFIX = "single";

    /**
     * The name of the property used in work directory for EAR webapps (in ear case).
     */
    protected static final String INEAR_WORK_WEBAPPS_DIR_SUFFIX = "ear";

    /**
     * Web service configuration properties : Files deployed
     */
    public static final String DESCRIPTORS = "jonas.service.web.descriptors";

    /**
     * Web service configuration properties : Autdeployed the files in these
     * directories
     */
    public static final String AUTOLOADDIR = "jonas.service.web.autoloaddir";

    /**
     * Web service configuration properties : Xml parsing with validation
     */
    public static final String PARSINGWITHVALIDATION = "jonas.service.web.parsingwithvalidation";

    /**
     * Length of the extension .war
     */
    private static final int WAR_EXTENSION_LENGTH = 4;

    /**
     * Web service configuration properties : Implementation of the web
     * container
     */
    public static final String CLASS = "jonas.service.web.class";
    
    /**
     * JOnAS configuration properties : development mode
     */
    private static final String DEVELOPMENT = "jonas.development";

    /**
     * Logger for this service.
     */
    private static Logger logger = null;

    /**
     * The name of the current jonas server
     */
    private static String nameOfServer = null;

    /**
     * Reference on the NamingManager.
     */
    private ContainerNaming naming;

    /**
     * Associates an URL of an unpacked WAR file to its classloader.
     */
    private Hashtable warLoaders = new Hashtable();

    /**
     * Associates an URL of a deployed WAR file to its classloader.
     */
    private Hashtable warBindings = new Hashtable();

    /**
     * Reference to a MBean server.
     */
    private MBeanServer mbeanServer = null; // Bug Tomcat 5 doesn't remove MBean
                                            // WebModule, normally the type is
                                            // "private"

    /**
     * List of the war names to load when starting the Web container Service.
     */
    private Vector warNames = new Vector();

    /**
     * List of the war deployed by the Web container Service.
     */
    private Vector warDeployed = new Vector();
    
    /**
     * Map of the unpacked WARs.
     */
    private Map unpackedWARs = new HashMap();

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

    /**
     * Name of the server
     */
    private String serverName = null;

    /**
     * Version of the server
     */
    private String serverVersion = null;

    /**
     * Reference to the WebServices service
     */
    private WebServicesService wsService = null;

    /**
     * Application Classloader
     */
    private ClassLoader appsClassLoader;
    
    /**
     * Reference on the DeployerLog which is the class that manage the
     * accesses to the log file (to remove the ear).
     */
    private DeployerLog warDeployerLog = null;
    
    /**
     * Reference to the cleaner.
     */
    private static WorkCleaner workCleaner = null;
    
    /**
     * True in development mode.
     */
    private String development;

    /**
     * Initialize the service.
     * @param ctx the configuration context of the service.
     * @throws ServiceException if the initialization failed.
     */
    protected void doInit(Context ctx) throws ServiceException {

        // Init the logger
        logger = Log.getLogger(Log.JONAS_WEB_PREFIX);

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

        ServiceManager sm = null;
        try {
            sm = ServiceManager.getInstance();
        } catch (Exception e) {
            String err = "Cannot get ServiceManager instance.";
            logger.log(BasicLevel.ERROR, err);
            throw new ServiceException(err, e);
        }

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

        // Get the WebServices service
        try {
            wsService = (WebServicesService) sm.getWebServicesService();
        } catch (ServiceException e) {
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "WebServices service not started");
            }
            //not started
            wsService = null;
        }

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

        WebManagerWrapper.setParsingWithValidation("true".equalsIgnoreCase(parsingMode));
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            if ("false".equalsIgnoreCase(parsingMode)) {
                logger.log(BasicLevel.DEBUG, "Web XML parsing without validation");
            } else {
                logger.log(BasicLevel.DEBUG, "Web XML parsing with validation");
            }
        }

        // Init the war names to be loaded when starting
        String descsValue = null;
        try {
            descsValue = (String) ctx.lookup(DESCRIPTORS);
        } catch (NamingException e) {
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "No " + DESCRIPTORS);
            }
        }
        if (descsValue != null) {
            StringTokenizer st = new StringTokenizer(descsValue, ",");
            while (st.hasMoreTokens()) {
                String fileName = st.nextToken().trim();
                warNames.add(fileName);
            }
        }
        // Add the wars of the jonas.service.web.autoloaddir property
        String dirValue = null;
        ArrayList autoDirs = new ArrayList();
        try {
            dirValue = (String) ctx.lookup(AUTOLOADDIR);
        } catch (NamingException e) {
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "No " + AUTOLOADDIR);
            }
        }
        if (dirValue != null) {
            StringTokenizer st = new StringTokenizer(dirValue, ",");
            while (st.hasMoreTokens()) {
                String dirName = st.nextToken().trim();
                addWars(dirName);
                autoDirs.add(dirName);
            }
        }
        // Build autoload directories
        File oFile;
        Iterator it = autoDirs.iterator();
        while (it.hasNext()) {
            String dirName = (String) it.next();
            try {
                oFile = new File(WEBAPPS_DIR, dirName);
                if (!oFile.exists()) {
                    oFile = new File(dirName);
                }
                if (oFile.exists()) {
                    autoloadDirectories.add(oFile.getCanonicalPath());
                }
            } catch (Exception e) {
                String err = "Error when trying to verify Webapps autoload directory : " + dirName;
                logger.log(BasicLevel.ERROR, err + " " + e.getMessage());
            }
        }

        //Init the naming manager
        try {
            naming = NamingManager.getInstance();
        } catch (NamingException e) {
            throw new ServiceException("Error when getting the reference to the Naming manager");
        }

        JProp jp = null;
        try {
            jp = JProp.getInstance();
        } catch (Exception e) {
            String err = "Error when trying to get jonas name ";
            logger.log(BasicLevel.ERROR, err + " " + e.getMessage());
            throw new ServiceException(err, e);
        }
        AbsJWebContainerServiceImpl.nameOfServer = jp.getValue("jonas.name", "jonas");
        
        // Get the execution mode
        development = jp.getValue(DEVELOPMENT, "false");
    }

    /**
     * Start the service.
     * @throws ServiceException if the startup failed.
     */
    protected void doStart() throws ServiceException {
        // url
        URL wepApps = null;
        try {
            wepApps = new File(WORK_WEBAPPS_DIR + File.separator + getJonasServerName() + File.separator
                    + SINGLE_WORK_WEBAPPS_DIR_SUFFIX).toURL();
        } catch (MalformedURLException mue) {
            throw new ServiceException("Error when trying to get the URL of the jonasroot/webapps directory", mue);
        }

        File fLog = new File(wepApps.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 {
            warDeployerLog = new DeployerLog(fLog);
        } catch (DeployerLogException e) {
            throw new ServiceException("Can not get a WebAppDeployerLog", e);
        }

        // create the war deployment work file control task
        WarCleanTask earCleanTask = new WarCleanTask(warDeployerLog);

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

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

        // force the cleaning now
        workCleaner.executeTasks();
        
        // Deploy all wars which are in descriptors section
        for (Enumeration e = warNames.elements(); e.hasMoreElements();) {
            String fileName = (String) e.nextElement();

            URL warURL = checkWarFile(fileName);

            Context contctx = null;
            try {
                contctx = new CompNamingContext(fileName);
                contctx.rebind("warURL", warURL);
                // No context root to rebind because not in ear case.
                // => no rebind("contextRoot", ...)
            } catch (NamingException ne) {
                String err = "Cannot start the WebContainerService";
                throw new ServiceException(err, ne);
            }
            try {
                registerWar(contctx);
            } catch (JWebContainerServiceException jwcse) {
                String err = "Cannot deploy the file '" + warURL + "' : " + jwcse.getMessage();
                logger.log(BasicLevel.WARN, err);
            }
        }

        try {
            // Register JWebContainerService MBean :
            // JWebContainerServiceImplMBean
            if (mbeanServer != null) {
                mbeanServer.registerMBean(this, JonasObjectName.webContainerService());
            }
        } catch (InstanceAlreadyExistsException iae) {
            String err = "Cannot start the WebContainerService Already Exists";
            throw new ServiceException(err, iae);
        } catch (MBeanRegistrationException mbre) {
            throw new ServiceException("Cannot start the WebContainerService (MBean registration error)", mbre);
        } catch (NotCompliantMBeanException ncmbe) {
            throw new ServiceException("Cannot start the WebContainerService (MBean Not compliant error)", ncmbe);
        }
    }

    /**
     * Stop the service.
     * @throws ServiceException if the stop failed.
     */
    protected void doStop() throws ServiceException {

        // Get all the deployed war which are not in an ear application file
        // and undeploy them.
        for (int i = warDeployed.size() - 1; i >= 0; i--) {
            War war = (War) warDeployed.elementAt(i);
            URL warURL = war.getWarURL();
            String fileName = warURL.getFile();
            try {
                // Test if the war is in an ear application.
                if (!war.isInEarCase()) {
                    Context ctx = new CompNamingContext(fileName);
                    ctx.rebind("warURL", warURL);
                    ctx.rebind("isEarCase", new Boolean(false));
                    unRegisterWar(ctx);
                }
            } catch (Exception e) {
                //We don't stop the process of undeploying the wars.
                //We only display an error in the logger.
                String err = "Error when undeploying the war :" + fileName;
                logger.log(BasicLevel.ERROR, err + e.getMessage());
            }
        }

        if (mbeanServer != null) {
            try {
                // unregister AbsJWebContainerServiceImpl MBean
                mbeanServer.unregisterMBean(JonasObjectName.webContainerService());
            } catch (Exception e) {
                logger.log(BasicLevel.ERROR, "Cannot stop the WebContainerService: " + e);
            }
        }
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "WebContainerService stopped");
        }
    }

    /**
     * Create the environment and delegate the operation to the implementation
     * of the web container.
     * @param ctx the context which contains the configuration in order to
     *        deploy a WAR.
     * @throws JWebContainerServiceException if the registration of the WAR
     *         failed.
     */
    protected abstract void doRegisterWar(Context ctx) throws JWebContainerServiceException;

    /**
     * Delegate the unregistration to the implementation of the web container.
     * @param ctx the context which contains the configuration in order to
     *        undeploy a WAR.
     * @throws JWebContainerServiceException if the unregistration failed.
     */
    protected abstract void doUnRegisterWar(Context ctx) throws JWebContainerServiceException;

    /**
     * Return the URL where warURL has been unpacked.
     * @param warURL the URL of the war
     * @param earAppName EAR Application name (can be null if not in EAR case)
     * @return the URL where warURL has been unpacked.
     * @throws JWebContainerServiceException when it is impossible to retrieve
     *         the unpacked URL.
     */
    protected URL getUnpackDir(URL warURL, String earAppName) throws JWebContainerServiceException {

        String destDir = WORK_WEBAPPS_DIR + File.separator + nameOfServer + File.separator;

        // Two cases :
        // war is alone --> unpack it in :
        //   WEBAPPS_DIR/servername/single/war_filename_timestamp/
        // war come from an ear --> unpack it in :
        //   WEBAPPS_DIR/servername/ear/ear_filename_timestamp/war_filename__timestamp/

        // location of unpacked War
        URL unpackedWarURL = null;

        String fileName = warURL.getFile();

        // War file
        if (new File(fileName).isFile()) {
            // Note : if the war is alone, earAppName is null.
            // ear case
            if (earAppName != null) {
                // The destination directory is built from the filename of the unpacked ear,
                // that is to say, the parent folder of the original war URL.
                String earFileName = earAppName;
                if ("true".equals(development)) {
                    earFileName = new File(warURL.getFile()).getParentFile().getName();
                }
                destDir += INEAR_WORK_WEBAPPS_DIR_SUFFIX + File.separator + earFileName + File.separator;
            } else {
                destDir += SINGLE_WORK_WEBAPPS_DIR_SUFFIX + File.separator;
            }

            String fTemp = new File(fileName).getName();
            if (earAppName == null && "true".equals(development)) {
                try {
                    destDir += FileManager.fileToTimeStampDir(warURL);
                } catch (FileManagerException e) {
                    throw new JWebContainerServiceException("Cannot get the timestamp of the file " + warURL + " : "
                            + e.getMessage());
                }    
            } else {
                destDir += fTemp.substring(0, fTemp.length() - WAR_EXTENSION_LENGTH); 
            }

            // Unpack the war into a directory
            JarTools.unpack(fileName, destDir);
        } else {
            // Directory (No unpack)
            destDir = fileName;
        }

        try {
            unpackedWarURL = new File(destDir).toURL();
        } catch (MalformedURLException mue) {
            throw new JWebContainerServiceException("Error", mue);
        }

        return unpackedWarURL;

    }

    /**
     * Return the class loader of the given warURL. Unpack the associated war
     * and build the loader if it's not in the cache.
     * @param warURL the url of the war we want to get the loader
     * @param earAppName the name of the ear application containing the war. May
     *        be null in non ear case.
     * @param parentLoader the ejb class loader of the ear. May be null in non
     *        ear case.
     * @return the class loader of the given warURL.
     * @throws JWebContainerServiceException if the process failed.
     */
    public URLClassLoader getClassLoader(URL warURL, String earAppName, ClassLoader parentLoader)
            throws JWebContainerServiceException {

        URLClassLoader loaderForCls = null;
        try {
            WebLoaderHolder holder = (WebLoaderHolder) warLoaders.get(warURL);
            if (holder != null) {
                loaderForCls = holder.getJonasWebLoader();
            }
        } catch (Exception e) {
            throw new JWebContainerServiceException("Error when getting '" + warURL + "' in cache", e);
        }

        if (loaderForCls == null) {
            // the war is not already used

            // Use the unpacked directory
            URL unpackedWarURL = getUnpackDir(warURL, earAppName);

            try {
                if (parentLoader != null) {
                    // ear case.
                    loaderForCls = new WebappClassLoader(unpackedWarURL, parentLoader);
                } else {
                    //Case of non-ear application : only a single war
                    loaderForCls = new WebappClassLoader(unpackedWarURL, appsClassLoader);
                }
            } catch (IOException ioe) {
                throw new JWebContainerServiceException(
                        "Cannot create WebAppClassLoader from '" + unpackedWarURL + "'", ioe);
            }

            // add the class loader in cache.
            try {
                WebLoaderHolder holder = new WebLoaderHolder(loaderForCls, null);
                warLoaders.put(warURL, holder);
            } catch (Exception e) {
                throw new JWebContainerServiceException("Error when adding '" + warURL + "' in cache", e);
            }
        }

        return loaderForCls;

    }

    /**
     * @param warURL the URL of the webapp
     * @return Returns the ClassLoader used to link a JNDI environnment to a webapp
     */
    public ClassLoader getContextLinkedClassLoader(URL warURL) {
        WebLoaderHolder holder = (WebLoaderHolder) warLoaders.get(warURL);
        if (holder != null) {
            return holder.getEnvWebLoader();
        }
        return null;
    }

    /**
     * Create the environment and delegate the operation to the implementation
     * of the web container.
     * @param ctx the context which contains the configuration in order to
     *        deploy a WAR.
     * @throws JWebContainerServiceException if the registration of the WAR
     *         failed.
     */
    private void registerWar(Context ctx) throws JWebContainerServiceException {
        // There are 6 possible parameters :
        //   - warURL is the URL of the war to deploy (required param).
        //   - parentClassLoader is the parent classloader of
        //     the web classloader (optional param).
        //   - earClassLoader is the ear classloader (optional param).
        //   - earURL is the URL of the ear (optional parameter :
        //     if earURL is set it means that we are in the ear case, else it
        //     means that we are in the non ear case).
        //   - altDD is the optional deployment descriptor (optional param).
        //   - contextRoot is the context root for the Web application
        //     (optional param).

        // Get the URL of the ear application file in the case of an
        // ear application, null otherwise.
        URL earURL = null;
        String contextRoot = null;
        String earAppName = null;
        try {
            earURL = (URL) ctx.lookup("earURL");
            contextRoot = (String) ctx.lookup("contextRoot");
            earAppName = EarServiceImpl.buildJ2eeApplicationName(earURL);
        } catch (NamingException e) {
            if (earURL != null || contextRoot != null) {
                String err = "Error while getting parameter from context param :" + e.getMessage();
                logger.log(BasicLevel.ERROR, err);
                throw new JWebContainerServiceException(err, e);
            } // else nothing to do : non-ear case.
        }

        // Get the URL of the war to deploy ...
        URL warURL = null;
        try {
            warURL = (URL) ctx.lookup("warURL");
        } catch (NamingException e) {
            String err = "Error while getting parameter from context param :" + e.getMessage();
            logger.log(BasicLevel.ERROR, err);
            throw new JWebContainerServiceException(err, e);
        }

        // ... and check if the war to deploy exists.
        File warFile = new File(warURL.getFile());
        if (!warFile.exists()) {
            String err = "registerWar: '" + warFile.getPath() + "' not found";
            logger.log(BasicLevel.ERROR, err);
            throw new JWebContainerServiceException(err);
        }

        // Check if the war to deploy is not already deployed.
        War war = getWar(warURL);
        if (war != null) {
            // The war is already deployed.
            String err = "Cannot deploy war '" + warURL.getFile() + "' is already deployed."
                    + " You must undeploy the war before a new deployment.";
            throw new JWebContainerServiceException(err);
        }

        URLClassLoader parentLoader = null;
        URLClassLoader earClassLoader = null;
        boolean isInEar = true;
        try {
            parentLoader = (URLClassLoader) ctx.lookup("parentClassLoader");
            earClassLoader = (URLClassLoader) ctx.lookup("earClassLoader");
        } catch (NamingException ne) {
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "Not an ear case");
            }
            isInEar = false;
            // exception occurs when the earClassLoader is not found.
        }

        // Check WsGen for webapp outside of an Ear
        //-----------------------------------------

        if (!isInEar) {
            try {
                ObjectName j2eeServer = J2eeObjectName.J2EEServer(this.getDomainName(), this.getJonasServerName());
                WsGenChecker wsgc = new WsGenChecker(this.mbeanServer, warFile.getCanonicalPath(), j2eeServer);
                wsgc.checkWsGen();
            } catch (Exception e) {
                throw new JWebContainerServiceException("Cannot apply WsGen on the war : " + warFile, e);
            }
        }

        //------------------------------------------

        // get the war class loader.
        URLClassLoader loaderForCls = getClassLoader(warURL, earAppName, parentLoader);

        // get the directory where the war have been unpacked
        URL unpackedWarURL = getUnpackDir(warURL, earAppName);
        
        // The file is unpacked, so log it (only in development mode)
        if (!isInEar && warFile.isFile() && "true".equals(development)) {
            try {
                warDeployerLog.addEntry(warFile, new File(unpackedWarURL.getFile()));
            } catch (Exception e) {
                String err = "Error while adding the " + warFile + " entry in the log file";
                logger.log(BasicLevel.ERROR, err + " : " + e.getMessage());
                throw new JWebContainerServiceException(err, e);
            }
        }

        // Only if the WebServicesService is started
        // And in non EAR case
        if (wsService != null && earClassLoader == null) {
            try {
                CompNamingContext contctx = null;
                try {
                    contctx = new CompNamingContext(unpackedWarURL.getFile());
                    contctx.rebind("unpackDir", unpackedWarURL.toExternalForm());
                    contctx.rebind("jarUrls", new URL[0]);
                    contctx.rebind("warUrls", new URL[] {warURL});
                    if (parentLoader != null) {
                        contctx.rebind("ejbClassLoader", parentLoader);
                    }
                } catch (NamingException e) {
                    String err = "Can not bind params for the WebServices service, "
                            + "Can't deploy Web Services Endpoint";
                    throw new JWebContainerServiceException(err, e);
                }
                wsService.deployWebServices(contctx);
            } catch (ServiceException se) {
                String err = "Error during the deployment of the WebServices of the War file '" + warURL + "'";
                logger.log(BasicLevel.ERROR, err + " : " + se.getMessage());
                throw new JWebContainerServiceException(err, se);
            }
        }

        // Get the deployment descriptor from file
        WebContainerDeploymentDesc webDD = null;
        try {
            webDD = WebManagerWrapper.getDeploymentDesc(warURL, loaderForCls, earClassLoader);
        } catch (WebContainerDeploymentDescException e) {
            String err = "Cannot read the deployment descriptors '" + warURL.getFile() + "'";
            logger.log(BasicLevel.ERROR, err + ": " + e);
            e.printStackTrace(System.err);
            throw new JWebContainerServiceException(err, e);
        }

        // Populate the java:comp/env (ENC) environment.
        URLClassLoader webClassLoader = null;
        try {
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "Populating environment of the file " + warURL.getFile());
            }
            Context ctxParam = new CompNamingContext(unpackedWarURL.getFile());
            ctxParam.rebind("DeploymentDesc", webDD);
            ctxParam.rebind("warName", unpackedWarURL.getFile());
            if (parentLoader == null) {
                webClassLoader = new SimpleWebappClassLoader(unpackedWarURL, appsClassLoader);
            } else {
                webClassLoader = new SimpleWebappClassLoader(unpackedWarURL, parentLoader);
            }
            ctxParam.rebind("parentCL", webClassLoader);
            // Do the populating of the java:comp/env (ENC) environment.
            setWebEnvironment(ctxParam);
        } catch (Exception e) {
            //populating environment failed.
            String err = "Error when populating ";
            logger.log(BasicLevel.ERROR, err + e.getMessage());
            throw new JWebContainerServiceException(err, e);
        }

        // TODO maybe to be removed ?
        WebLoaderHolder holder = (WebLoaderHolder) warLoaders.get(warURL);
        holder.setEnvWebLoader(webClassLoader);

        // Set the right context root for the web application, the priority is
        // the following :
        //    1 - context-root of application.xml
        //    2 - context-root of jonas-web.xml
        //    3 - context-root is the name of the file without .war.
        if (earClassLoader == null && contextRoot == null) {
            String cRoot = webDD.getContextRoot();
            if (cRoot == null) {
                String file = warFile.getName();
                if (file.toLowerCase().endsWith(".war")) {
                    contextRoot = file.substring(0, file.length() - WAR_EXTENSION_LENGTH);
                } else {
                    //It's a directory which is deployed
                    contextRoot = file.substring(0, file.length());
                }
            } else {
                contextRoot = cRoot;
            }
        }

        // ensure context-root is valid (remove starting /)
        if (contextRoot.startsWith("/") && !contextRoot.equals("/")) {
            logger.log(BasicLevel.WARN, "Context-Root '" + contextRoot + "' contains invalid starting / in the name. Fixing it.");
            int c = 0;
            while (contextRoot.charAt(c) == '/') {
                c++;
            }
            contextRoot = contextRoot.substring(c);
        }


        // Set the name of the host where to deploy the war if it is
        // specified in the jonas-web.xml.
        String hostName = webDD.getHost();

        // Check if the context to deploy is not already deployed.
        List deployedWars = getWar(contextRoot);
        for (Iterator itDeployed = deployedWars.iterator(); itDeployed.hasNext();) {
            War deployedWar = (War) itDeployed.next();
            String hostDeployed = deployedWar.getHostName();
            if ((hostDeployed == null && hostName == null) || (hostDeployed != null && hostDeployed.equals(hostName))) {
                // The war is already deployed.
                String err = "Cannot deploy war '" + warURL.getFile() + "' is already deployed with the context '"
                        + contextRoot + "'." + " You must undeploy the war before a new deployment.";
                throw new JWebContainerServiceException(err);
            }
        }

        // Context classloader must follow the java2 delegation model ?
        boolean java2DelegationModel = webDD.getJava2DelegationModel();

        // Create War
        war = new War(warURL, earURL, hostName, contextRoot, java2DelegationModel, webDD.getXmlContent(), webDD
                .getJOnASXmlContent(), webDD.getServletsName());

        // Configure JACC
        PermissionManager permissionManager = null;
        try {
            boolean removePContext = true;
            // Policy context is already existing (created by ear service), no need to remove it
            if (earClassLoader != null) {
                removePContext = false;
            }
            permissionManager = new PermissionManager(webDD, war.getContextId(), removePContext);
            permissionManager.translateServletDeploymentDescriptor();
            // if not in ear case, commit the policy configuration, else it is done
            // by EAR service after linking all policy configuration objects
            if (earClassLoader == null) {
                permissionManager.commit();
            }
        } catch (Exception e) {
            e.printStackTrace();
            String err = "Cannot build permission manager object for the webapp '" + unpackedWarURL + "' : ";
            logger.log(BasicLevel.ERROR, err + e.getMessage());
            throw new JWebContainerServiceException(err, e);
        }

        // Configure the context to give to doRegisterWar ...
        Context ctxParam = null;
        try {
            ctxParam = new CompNamingContext(unpackedWarURL.getFile());
            ctxParam.rebind("warURL", warURL);
            if (earURL != null) {
                ctxParam.rebind("earURL", earURL);
            }
            ctxParam.rebind("unpackedWarURL", unpackedWarURL);
            ctxParam.rebind("parentCL", webClassLoader);
            if (hostName != null) {
                ctxParam.rebind("hostName", hostName);
            }
            ctxParam.rebind("contextRoot", contextRoot);
            if (earAppName != null) {
                ctxParam.rebind("earAppName", earAppName);
            }
            ctxParam.rebind("jonasDD", webDD.getJOnASXmlContent());
            ctxParam.rebind("java2DelegationModel", new Boolean(java2DelegationModel));
            ctxParam.rebind("permissionManager", permissionManager);
        } catch (NamingException e) {
            String err = "Error when deploying the war  '" + unpackedWarURL.getFile() + "'";
            logger.log(BasicLevel.ERROR, err + e.getMessage());
            throw new JWebContainerServiceException(err, e);
        }

        war.setPermissionManager(permissionManager);

        // ... and delegate the registration of the war to the wrapper.
        doRegisterWar(ctxParam);

        // Finaly indicates that the war is deployed.
        warDeployed.addElement(war);
        warBindings.put(warURL, webClassLoader);
        unpackedWARs.put(warURL, unpackedWarURL);

        try {
            // Register Ear MBean : EarMBean
            if (mbeanServer != null) {
                mbeanServer.registerMBean(war, JonasObjectName.war(warURL.getFile()));
            }
        } catch (Exception ware) {
            logger.log(BasicLevel.ERROR, "Can not register the MBean for the war" + unpackedWarURL + " :"
                    + ware.getMessage());
        }

        // Complete the Deployment of the WebServices
        // Only if the WebServicesService is started
        // And in non EAR case
        if (wsService != null && earClassLoader == null) {
            try {
                CompNamingContext contctx = null;
                try {
                    contctx = new CompNamingContext(unpackedWarURL.getFile());
                    contctx.rebind(WebServicesService.CLASSLOADER_CTX_PARAM, loaderForCls);
                    contctx.rebind(WebServicesService.PARENT_OBJECTNAME_CTX_PARAM , ctxParam.lookup("WebModule"));
                    contctx.rebind(WebServicesService.ISINEAR_CTX_PARAM, Boolean.FALSE);
                } catch (NamingException e) {
                    String err = "Can not bind params for the WebServices service, "
                            + "can't complete deployment of Web Services Endpoints";
                    throw new JWebContainerServiceException(err, e);
                }
                wsService.completeWSDeployment(contctx);
            } catch (ServiceException se) {
                String err = "Error during the deployment of the WebServices of the War file '" + warURL + "'";
                logger.log(BasicLevel.ERROR, err + " : " + se.getMessage());
                throw new JWebContainerServiceException(err, se);
            }
        }

        StringBuffer txtInfo = new StringBuffer("War " + warURL.getFile() + " available at the context ");
        if (!contextRoot.startsWith("/")) {
            txtInfo.append("/");
        }
        txtInfo.append(contextRoot);

        if (hostName != null) {
            txtInfo.append(" on the host ");
            txtInfo.append(hostName);
        }
        txtInfo.append(".");
        logger.log(BasicLevel.INFO, txtInfo.toString());

        // Remove the DD cache (WebServices)
        if (wsService != null && earClassLoader == null) {
            wsService.removeCache(loaderForCls);
        }
    }

    /**
     * Register a WAR by delegating the operation to the registerWar() method.
     * This is used for JMX management.
     * @param fileName the name of the war to deploy.
     * @throws RemoteException if rmi call failed.
     * @throws JWebContainerServiceException if the registration failed.
     */
    public void registerWarMBean(String fileName) throws RemoteException, JWebContainerServiceException {
        // convert the file name to the appropriate url and check if the file
        // exists.
        URL warURL = checkWarFile(fileName);

        // create the context to call the registerWar method.
        Context ctx = null;
        try {
            ctx = new CompNamingContext(fileName);
            ctx.rebind("warURL", warURL);
        } catch (NamingException e) {
            String err = "Error when deploying the war  '" + fileName + "'";
            logger.log(BasicLevel.ERROR, err + e.getMessage());
            throw new JWebContainerServiceException(err, e);
        }

        // call the registerWar method.
        registerWar(ctx);
    }

    /**
     * Set the environment of the web container inside the given context.
     * @param ctxParam the java:comp/env/ environment where is stored the values
     *        of the web container environment.
     * @throws JWebContainerServiceException if the populating of the
     *         environment failed.
     */
    private void setWebEnvironment(Context ctxParam) throws JWebContainerServiceException {

        WebContainerDeploymentDesc dd = null;
        String warName = null;
        ClassLoader parentClassLoader = null;

        /*
         * get the parameters - Deployment desc of xml - Name of the war -
         * Parent Class loader
         */
        try {
            dd = (WebContainerDeploymentDesc) ctxParam.lookup("DeploymentDesc");
            warName = (String) ctxParam.lookup("warName");
        } catch (NamingException e) {
            String err = "Error while getting parameter from context param ";
            logger.log(BasicLevel.ERROR, err + e.getMessage());
            throw new JWebContainerServiceException(err, e);
        }

        //Get the parentCL
        try {
            parentClassLoader = (ClassLoader) ctxParam.lookup("parentCL");
        } catch (NamingException e) {
            String err = "Error while getting parameter from context param ";
            logger.log(BasicLevel.ERROR, err + e.getMessage());
            throw new JWebContainerServiceException(err, e);
        }

        //Create the java:comp/env entry and bind the entries
        try {
            // Create a JNDI context for this war java:comp/env
            Context javaCtx = naming.createEnvironmentContext(warName);
            naming.setComponentContext(javaCtx, parentClassLoader);
            Context envCtx = javaCtx.createSubcontext("comp/env");

            // War Environment entries
            EnvEntryDesc[] envt = dd.getEnvEntryDesc();
            for (int i = 0; i < envt.length; i++) {
                // get information in descriptor
                String name = envt[i].getName();
                Object obj = envt[i].getValue();

                // register object in JNDI
                if (logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG, warName + ": Binding object " + name + " -> " + obj);
                }
                envCtx.rebind(name, obj);
            }

            // Resource References
            ResourceRefDesc[] resref = dd.getResourceRefDesc();
            for (int i = 0; i < resref.length; i++) {
                // get information in descriptor
                String name = resref[i].getName();
                String type = resref[i].getTypeName();
                String resname = resref[i].getJndiName();
                // register object in JNDI
                if (logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG, warName + ": Linking resource " + name + " -> " + resname);
                }

                if (type.equalsIgnoreCase("java.net.URL")) {
                    // Specify the factory to use with the right URL
                    Reference ref = new Reference("java.net.URL", "org.objectweb.jonas_lib.naming.factory.URLFactory",
                            null);
                    StringRefAddr refAddr = new StringRefAddr("url", resname);
                    ref.add(refAddr);
                    envCtx.rebind(name, ref);
                } else {
                    // build the LinkRef that will be registered:
                    // FactoryClassName = null, size = 1, refAddr = resname.
                    LinkRef lref = new LinkRef(resname);
                    envCtx.rebind(name, lref);
                }
            }

            // Resource Environment References
            ResourceEnvRefDesc[] resEnvref = dd.getResourceEnvRefDesc();
            for (int i = 0; i < resEnvref.length; i++) {
                // get information in descriptor
                String name = resEnvref[i].getName();
                String resname = resEnvref[i].getJndiName();
                LinkRef lref = new LinkRef(resname);

                if (logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG, warName + ": Linking resource environment " + name + " -> " + resname);
                }
                envCtx.rebind(name, lref);
            }

            // EJB References
            EjbRefDesc[] ejbref = dd.getEjbRefDesc();
            for (int i = 0; i < ejbref.length; i++) {
                // get information in descriptor
                String name = ejbref[i].getEjbRefName();
                String ejbname = null;
                ejbname = ejbref[i].getJndiName();

                LinkRef lref = new LinkRef(ejbname);

                if (logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG, warName + ": Linking ejb " + name + " -> " + ejbname);
                }
                envCtx.rebind(name, lref);
            }

            // EJB Local Refs
            // We use here ejb-link tag. This should be used also for
            // ejb-ref when we are able to manage references to
            // another jar file.
            EjbLocalRefDesc[] ejblocalref = dd.getEjbLocalRefDesc();
            for (int i = 0; i < ejblocalref.length; i++) {
                String name = ejblocalref[i].getEjbRefName();
                String ejbname = ejblocalref[i].getJndiLocalName();
                LinkRef lref = new LinkRef(ejbname);
                if (logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG, warName + ": Linking ejb " + name + " -> " + ejbname);
                }
                envCtx.rebind(name, lref);
            }

            // Message Destination References
            MessageDestinationRefDesc[] mdref = dd.getMessageDestinationRefDesc();
            for (int i = 0; i < mdref.length; i++) {
                // get information in descriptor
                String name = mdref[i].getMessageDestinationRefName();
                String mdname = null;
                mdname = mdref[i].getJndiName();

                LinkRef lref = new LinkRef(mdname);

                if (logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG, warName + ": Linking message-destination " + name + " -> " + mdname);
                }
                envCtx.rebind(name, lref);
            }

            // Service Ref
            // We bind a Reference when full configuration is provided
            // Otherwise we bind only a LinkRef in JNDI

            // get the JServiceFactory
            JServiceFactory factory = null;

            ServiceRefDesc[] serviceRefs = dd.getServiceRefDesc();
            for (int i = 0; i < serviceRefs.length; i++) {

                if (factory == null) {
                    factory = JServiceFactoryFinder.getJOnASServiceFactory();
                }

                // Create the Service from the ServiceRef description
                String name = serviceRefs[i].getServiceRefName();
                // create a full Reference
                Reference ref = factory.getServiceReference(serviceRefs[i], parentClassLoader);
                envCtx.rebind(name, ref);
                if (logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG, "Adding service-ref 'java:comp/env/" + name + "'");
                }
            }

        } catch (NamingException e) {
            String err = "Error while populating environment of the war file " + warName;
            logger.log(BasicLevel.ERROR, err + "  :" + e.getMessage());
            throw new JWebContainerServiceException(err, e);
        }
    }

    /**
     * Delegate the unregistration to the implementation of the web container
     * and delete the environment associated to the WAR file.
     * @param ctx the context which contains the configuration in order to
     *        undeploy a WAR.
     * @throws JWebContainerServiceException if the unregistration failed.
     */
    private void unRegisterWar(Context ctx) throws JWebContainerServiceException {

        // Gets the 2 parameters :
        // warURL, isEarCase, hostName and contextRoot.
        URL warURL = null;
        boolean isEarCase = true;
        try {
            warURL = (URL) ctx.lookup("warURL");
            isEarCase = ((Boolean) ctx.lookup("isEarCase")).booleanValue();
        } catch (NamingException e) {
            String err = "Error while getting parameter from context param.";
            logger.log(BasicLevel.ERROR, err + e.getMessage());
            throw new JWebContainerServiceException(err, e);
        }

        // get the file name for displaying message.
        String fileName = warURL.getFile();

        // Check if the war is deployed.
        War war = null;
        war = getWar(warURL);
        if (war == null) {
            String err = "Cannot undeploy war: '" + fileName + "' is not deployed.";
            logger.log(BasicLevel.ERROR, err);
            throw new JWebContainerServiceException(err);
        }

        // Check if the war can be undeployed (2 case authorized) :
        //    - case EAR and war is in an EAR.
        //    - case non EAR and war isn't in an EAR.
        if (isEarCase != war.isInEarCase()) {
            String err = "Cannot undeploy war: '" + fileName
                    + "' it is in an ear application. You must undeploy the ear associated.";
            logger.log(BasicLevel.ERROR, err);
            throw new JWebContainerServiceException(err);
        }

        // Call the specific method of the implementation of the web container.
        try {
            String hostName = war.getHostName();
            if (hostName != null) {
                ctx.rebind("hostName", war.getHostName());
            }
            ctx.rebind("contextRoot", war.getContextRoot());
            ctx.rebind("webClassLoader", warBindings.get(warURL));
        } catch (NamingException e) {
            String err = "Error when undeploying the war  '" + fileName + "'";
            logger.log(BasicLevel.ERROR, err + e.getMessage());
            throw new JWebContainerServiceException(err, e);
        }

        // Remove permission manager
        PermissionManager permissionManager = war.getPermissionManager();
        try {
            permissionManager.delete();
            permissionManager = null;
        } catch (PermissionManagerException pme) {
            logger.log(BasicLevel.ERROR, "Cannot remove permission manager for file '" + fileName + "'.", pme);
        }

        doUnRegisterWar(ctx);

        // undeploy webservices
        if (wsService != null) {
            wsService.undeployWebServices(ctx);
        }

        // Remove the context.
        URLClassLoader loader = (URLClassLoader) warBindings.remove(warURL);
        naming.unSetComponentContext(loader);

        // Remove classloader
        warLoaders.remove(warURL);

        // Indicates that the war is not in the deployed war.
        // (here we are sure that the war is not null and isn't in an ear).
        warDeployed.removeElement(war);
        unpackedWARs.remove(warURL);

        //unregister mbean
        if (mbeanServer != null) {
            try {
                // unregister Ear MBean : EarMBean
                mbeanServer.unregisterMBean(JonasObjectName.war(fileName));
            } catch (Exception e) {
                logger
                        .log(BasicLevel.ERROR, "Cannot remove the MBean for the war " + fileName + " : "
                                + e.getMessage());
            }
        }

        logger.log(BasicLevel.INFO, "War " + fileName + " no longer available");
    }

    /**
     * Unregister a WAR by delegating the operation to the unRegisterWar()
     * method. This is used for JMX management.
     * @param fileName the name of the war to undeploy.
     * @throws RemoteException if rmi call failed.
     * @throws JWebContainerServiceException if the unregistration failed.
     */
    public void unRegisterWarMBean(String fileName) throws RemoteException, JWebContainerServiceException {

        // Convert the given file name to an url and check if the war is
        // deployed ...
        URL warURL = checkWarDeployed(fileName);

        // ... and do the undeployment.
        Context ctx = null;
        try {
            ctx = new CompNamingContext(fileName);
            ctx.rebind("warURL", warURL);
            ctx.rebind("isEarCase", new Boolean(false));
        } catch (NamingException e) {
            String err = "Error when undeploying the war file '" + fileName + "'";
            logger.log(BasicLevel.ERROR, err + e.getMessage());
            throw new JWebContainerServiceException(err, e);
        }
        unRegisterWar(ctx);
    }

    /**
     * Deploy the given wars of an ear file with the specified parent
     * classloader (ejb classloader or ear classloader). (This method is only
     * used for the ear applications, not for the web applications).
     * @param ctx the context containing the configuration to deploy the wars.
     *        <BR>This context contains the following parameters :<BR>- urls
     *        the list of the urls of the wars to deploy. <BR>- earURL the URL
     *        of the ear application file. <BR>- parentClassLoader the parent
     *        classLoader of the wars. <BR>- earClassLoader the ear classLoader
     *        of the j2ee app. <BR>- altDDs the optional URI of deployment
     *        descriptor. <BR>- contextRoots the optional context root of the
     *        wars. <BR>
     * @throws JWebContainerServiceException if an error occurs during the
     *         deployment.
     */
    public void deployWars(Context ctx) throws JWebContainerServiceException {
        // Gets the parameters from the context :
        //    - urls the list of the urls of the wars to deploy.
        //    - earURL the URL of the ear application file.
        //    - parentClassLoader the parent classLoader of the wars.
        //    - earClassLoader the ear classLoader of the j2ee app.
        //    - altDDs the optional URI of deployment descriptor.
        //    - contextRoots the optional context root of the wars.
        URL[] urls = null;
        URL earURL = null;
        ClassLoader parentClassLoader = null;
        ClassLoader earClassLoader = null;
        URL[] altDDs = null;
        String[] contextRoots = null;
        try {
            urls = (URL[]) ctx.lookup("urls");
            earURL = (URL) ctx.lookup("earURL");
            parentClassLoader = (ClassLoader) ctx.lookup("parentClassLoader");
            earClassLoader = (ClassLoader) ctx.lookup("earClassLoader");
            altDDs = (URL[]) ctx.lookup("altDDs");
            contextRoots = (String[]) ctx.lookup("contextRoots");
        } catch (NamingException e) {
            String err = "Error while getting parameter from context param ";
            logger.log(BasicLevel.ERROR, err + e.getMessage());
            throw new JWebContainerServiceException(err, e);
        }
        //webDDManager.setAltDD(earClassLoader, urls, altDDs);

        // Deploy all the wars of the ear application.
        for (int i = 0; i < urls.length; i++) {
            // Get the name of a war to deploy.
            String fileName = urls[i].getFile();
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "Deploy war '" + 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("warURL", urls[i]);
                contctx.rebind("parentClassLoader", parentClassLoader);
                contctx.rebind("earClassLoader", earClassLoader);
                contctx.rebind("earURL", earURL);
                if (altDDs[i] != null) {
                    contctx.rebind("altDD", altDDs[i]);
                }
                if (contextRoots[i] != null) {
                    contctx.rebind("contextRoot", contextRoots[i]);
                }
                registerWar(contctx);
            } catch (Exception e) {
                // A war is corrupted so undeploy all the deployed war
                // of the ear application.
                logger.log(BasicLevel.ERROR, "Error when deploying '" + fileName + "'");
                logger.log(BasicLevel.ERROR, e.getMessage());
                logger.log(BasicLevel.ERROR, "Undeploy war of the ear application");

                for (int j = 0; j < i; j++) {
                    String warFileName = urls[j].getFile();
                    try {
                        // Try to undeploy a war of the ear application.
                        CompNamingContext context = new CompNamingContext(warFileName);
                        context.rebind("warURL", urls[j]);
                        context.rebind("isEarCase", new Boolean(true));
                        unRegisterWar(context);
                    } catch (Exception ex) {
                        // Cannot undeploy a war of the ear application
                        // So there is an error message.
                        logger.log(BasicLevel.ERROR, "Error when undeploying '" + warFileName + "'");
                        logger.log(BasicLevel.ERROR, ex.getMessage());
                        logger.log(BasicLevel.ERROR, "Cannot undeploy war of the ear application");
                    }

                }
                throw new JWebContainerServiceException("Error during the deployment", e);
            }
        }
    }

    /**
     * Undeploy the given wars of an ear file with the specified parent
     * classloader (ejb classloader or ear classloader). (This method is only
     * used for the ear applications, not for the war applications).
     * @param urls the list of the urls of the wars to undeploy.
     */
    public void unDeployWars(URL[] urls) {
        for (int i = 0; i < urls.length; i++) {
            String warFileName = urls[i].getFile();
            try {
                // Try to undeploy a war of the ear application.
                CompNamingContext context = new CompNamingContext(warFileName);
                context.rebind("warURL", urls[i]);
                context.rebind("isEarCase", new Boolean(true));
                unRegisterWar(context);
            } catch (Exception ex) {
                // Cannot undeploy a war of the ear application
                // So there is an error message.
                logger.log(BasicLevel.ERROR, "Error when undeploying '" + warFileName + "'");
                logger.log(BasicLevel.ERROR, ex.getMessage());
                logger.log(BasicLevel.ERROR, "Cannot undeploy war of the ear application");
            }
        }
    }

    /**
     * Get the war identified by its URL (.war).
     * @param url the URL of the war to get.
     * @return the war indentified by its URL, or null if the war is not found.
     */
    public War getWar(URL url) {
        Enumeration wars = warDeployed.elements();
        while (wars.hasMoreElements()) {
            War war = (War) wars.nextElement();
            if (war.getWarURL().equals(url)) {
                return war;
            }
        }
        return null;
    }

    /**
     * Get a list of wars identified by their Context.
     * @param pContext the context of the war to get.
     * @return the list of wars indentified by their Context, or an empty list
     *         if no wars are found.
     */
    private List getWar(String pContext) {
        List checkDeployed = new ArrayList();
        Enumeration wars = warDeployed.elements();
        while (wars.hasMoreElements()) {
            War war = (War) wars.nextElement();
            if (war.getContextRoot().equals(pContext)) {
                checkDeployed.add(war);
            }
        }
        return checkDeployed;
    }

    /**
     * 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 ClassLoader of the ear application to remove
     *        from the cache.
     */
    public void removeCache(ClassLoader earClassLoader) {
        WebManagerWrapper.removeCache(earClassLoader);
    }

    /**
     * Check if the specified file name correspond to a file which is located
     * relatively to where the JOnAS server is launched or in the
     * $JONAS_BASE/web-apps.
     * @param fileName the file to check if it exists.
     * @return the URL of the file found (either relatively to the JOnAS server
     *         or to the $JONAS_BASE/web-apps).
     * @throws JWebContainerServiceException if the specified file does't
     *         exists.
     */
    private URL checkWarFile(String fileName) throws JWebContainerServiceException {

        File f = null;
        try {
            f = new File(fileName).getCanonicalFile();
            if (!f.exists()) {
                boolean found = false;
                String warFileName = null;
                // Check also in JONAS_BASE/webapps directory
                warFileName = WEBAPPS_DIR + File.separator + fileName;
                f = new File(warFileName).getCanonicalFile();
                found = f.exists();
                if (found) {
                    fileName = f.getPath();
                } else {
                    String err = "File '" + f.getPath() + "' not found";
                    logger.log(BasicLevel.ERROR, err);
                    throw new JWebContainerServiceException(err);
                }
            }
        } catch (IOException e) {
            String err = "Invalid war file name '" + fileName;
            logger.log(BasicLevel.ERROR, err);
            throw new JWebContainerServiceException(err, e);
        }

        URL warURL = null;
        try {
            warURL = f.toURL();
        } catch (MalformedURLException e) {
            String err = "Invalid war file name '" + fileName + "'.";
            logger.log(BasicLevel.ERROR, err + e.getMessage());
            throw new JWebContainerServiceException(err, e);
        }
        return warURL;
    }

    /**
     * Check if the specified file is already deployed in the JOnAS server and
     * return the URL of this deployed war file.
     * @param fileName the name of the WAR file to check.
     * @return the URL of the deployed war file.
     * @throws JWebContainerServiceException if the file name doesn't correspond
     *         to a deployed war.
     */
    private URL checkWarDeployed(String fileName) throws JWebContainerServiceException {

        URL url = null;
        try {
            Enumeration wars = warDeployed.elements();
            while (wars.hasMoreElements()) {
                War war = (War) wars.nextElement();
                url = (new File(fileName).getCanonicalFile()).toURL();
                if (war.getWarURL().equals(url)) {
                    return url;
                }
            }
            String warFileName = WEBAPPS_DIR + File.separator + fileName;
            wars = warDeployed.elements();
            while (wars.hasMoreElements()) {
                War war = (War) wars.nextElement();
                url = (new File(warFileName).getCanonicalFile()).toURL();
                if (war.getWarURL().equals(url)) {
                    return url;
                }
            }

            String err = "Cannot undeploy war: '" + fileName + "' is not deployed.";
            logger.log(BasicLevel.ERROR, err);
            throw new JWebContainerServiceException(err);

        } catch (MalformedURLException e) {
            String err = "Invalid war file name '" + fileName + "'.";
            logger.log(BasicLevel.ERROR, err + e.getMessage());
            throw new JWebContainerServiceException(err, e);
        } catch (IOException e) {
            String err = "Invalid war file name '" + fileName;
            logger.log(BasicLevel.ERROR, err);
            throw new JWebContainerServiceException(err, e);
        }
    }

    /**
     * @return current number of wars deployed in the JOnAS server
     */
    public Integer getCurrentNumberOfWars() {
        return new Integer(warDeployed.size());
    }

    /**
     * Return the list of installed web applications. The WAR files or the
     * directories with expanded web application are searched in
     * JONAS_BASE/webapps and all webapps directories 'autoload'.
     * @return The list of WAR files or the directories with expanded web
     *         application found
     * @throws Exception if the list can't be retrieved
     */
    public List getInstalledWars() throws Exception {
        // get WAR files found in JONAS_BASE/webapps
        ArrayList al = JModule.getInstalledContainersInDir(WEBAPPS_DIR, JModule.WAR_EXTENSION, JModule.WAR_CHILD_DIR,
                JModule.WAR_CONFIRM_FILE);
        // get WAR files found in all autoload directories
        for (Iterator it = autoloadDirectories.iterator(); it.hasNext();) {
            al.addAll(JModule.getInstalledContainersInDir((String) it.next(), JModule.WAR_EXTENSION,
                    JModule.WAR_CHILD_DIR, JModule.WAR_CONFIRM_FILE));
        }
        return al;
    }

    /**
     * This method is added temporarily. It will disapear when Wars will have
     * their associated MBeans (when Wars will become manageable)
     * @return the names of the wars currently deployed in the JOnAS server
     */
    public Set getWarNames() {
        HashSet names = new HashSet();
        for (Enumeration wars = warDeployed.elements(); wars.hasMoreElements();) {
            War war = (War) wars.nextElement();
            URL warURL = war.getWarURL();
            names.add(warURL.getFile());
        }
        return names;
    }

    /**
     * Add wars from a directory (war can be directories with WEB-INF/
     * directories) 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/webapps/ directory.
     * @param dirPath the path to the directory containing the wars to load.
     */
    private void addWars(String dirPath) {
        boolean found = false;

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

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

    /**
     * Add the wars of the specified directory.
     * @param dir the directory from which the wars are loaded.
     * @throws JWebContainerServiceException if the argument is not a directory
     */
    private void addWarsFrom(File dir) throws JWebContainerServiceException {
        try {
            if (dir.isDirectory()) {
                File[] files = dir.listFiles();
                for (int i = 0; i < files.length; i++) {
                    if (files[i].isFile()) {
                        if (files[i].getPath().toLowerCase().endsWith(".war")) {
                            // War file
                            warNames.add(files[i].getCanonicalPath());
                        }
                    } else {
                        // Directory webapp
                        warNames.add(files[i].getCanonicalPath());
                    }
                }
            }
        } catch (IOException e) {
            String err = "Invalid file name '" + dir.getPath();
            logger.log(BasicLevel.ERROR, err);
            throw new JWebContainerServiceException(err, e);
        }
    }

    /**
     * Test if the specified filename is already deployed or not
     * @param fileName the name of the war file.
     * @return true if the war is deployed, else false.
     */
    public boolean isWarLoaded(String fileName) {

        URL url = null;
        boolean isLoaded = false;
        try {
            // Absolute filename
            try {
                url = new File(fileName).getCanonicalFile().toURL();
                //Check if the war is already deployed or not
                if (getWar(url) != null) {
                    isLoaded = true;
                } else {
                    // Not found force to test in relative Webapps directory
                    url = null;
                }
            } catch (Exception e) {
                url = null;
            }
            // Relative filename
            if (url == null) {
                url = new File(WEBAPPS_DIR + File.separator + fileName).getCanonicalFile().toURL();
                //Check if the war is already deployed or not
                if (getWar(url) != null) {
                    isLoaded = true;
                }
            }
        } catch (Exception e) {
            String err = "Can not found if the war is deployed or not";
            logger.log(BasicLevel.ERROR, err);
            return false;
        }

        return isLoaded;
    }

    /**
     * Return the list of all loaded web applications.
     * @return The list of deployed web applications
     */
    public List getDeployedWars() {
        ArrayList al = new ArrayList();
        for (Enumeration wars = warDeployed.elements(); wars.hasMoreElements();) {
            War war = (War) wars.nextElement();
            URL warURL = war.getWarURL();
            al.add(warURL.getFile());
        }
        return al;
    }

    /**
     * Return the list of installed web applications ready to deploy.
     * @return The list of deployable web applications
     * @throws Exception if the list can't be retrieved
     */
    public List getDeployableWars() throws Exception {
        List al = getInstalledWars();
        al.removeAll(getDeployedWars());
        return al;
    }

    /**
     * Return the list of "autoload" directories for web applications.
     * @return The list of all "autoload" directories
     */
    public List getAutoloadDirectories() {
        ArrayList al = new ArrayList();
        for (Iterator it = autoloadDirectories.iterator(); it.hasNext();) {
            try {
                al.add(new File((String) it.next()).toURL().getPath());
            } catch (Exception e) {
                if (logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG, "Can't get autoload directories " + e.getMessage());
                }
                // none
            }
        }
        return al;
    }

    /**
     * Return the WebApps directory.
     * @return The WebApps directory
     */
    public String getWebappsDirectory() {
        String sRet = null;
        try {
            sRet = (new File(WEBAPPS_DIR)).toURL().getPath();
        } catch (Exception e) {
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "Can't get webapps directory " + e.getMessage());
            }
            // none
        }
        return sRet;
    }

    /**
     * Gets the name of the server which is the web container
     * @return the name of the server which is the web container
     */
    public String getServerName() {
        if (serverName == null) {
            updateServerInfos();
        }
        return serverName;
    }

    /**
     * Gets the version of the server which is the web container
     * @return the version of the server which is the web container
     */
    public String getServerVersion() {
        if (serverVersion == null) {
            updateServerInfos();
        }
        return serverVersion;
    }

    /**
     * Test if the specified unpack name is already deployed or not. This method
     * is defined in the {@link JWebContainerService} interface.
     * @param unpackName the name of the war file.
     * @return true if the war is deployed, else false.
     */
    public boolean isWarDeployedByWorkName(final String unpackName) {
        // for each unpacked wars
        Collection c = unpackedWARs.values();
        for (Iterator iterator = c.iterator(); iterator.hasNext();) {
            URL url = (URL) iterator.next();
            if (new File(url.getFile()).getName().equals(unpackName)) {
                return true;
            }
        }

        // not found
        return false;
    }
    
    /**
     * Update info of the serverName and serverVersion
     */
    protected abstract void updateServerInfos();

    /**
     * Return the Default host name of the web container.
     * @return the Default host name of the web container.
     * @throws JWebContainerServiceException when it is impossible to get the
     *         Default Host.
     */
    public abstract String getDefaultHost() throws JWebContainerServiceException;

    /**
     * Return the Default HTTP port number of the web container (can be null if
     * multiple HTTP connector has been set).
     * @return the Default HTTP port number of the web container.
     * @throws JWebContainerServiceException when it is impossible to get the
     *         Default Http port.
     */
    public abstract String getDefaultHttpPort() throws JWebContainerServiceException;

    /**
     * Return the Default HTTPS port number of the web container (can be null if
     * multiple HTTPS connector has been set).
     * @return the Default HTTPS port number of the web container.
     * @throws JWebContainerServiceException when it is impossible to get the
     *         Default Https port.
     */
    public abstract String getDefaultHttpsPort() throws JWebContainerServiceException;

    /**
     * @return Returns the logger.
     */
    protected static Logger getLogger() {
        return logger;
    }

    /**
     * @return Returns the naming.
     */
    protected ContainerNaming getNaming() {
        return naming;
    }

    /**
     * @return Returns the mbeanServer.
     */
    protected MBeanServer getMbeanServer() {
        return mbeanServer;
    }

    /**
     * @param serverName The serverName to set.
     */
    protected void setServerName(String serverName) {
        this.serverName = serverName;
    }

    /**
     * @param serverVersion The serverVersion to set.
     */
    protected void setServerVersion(String serverVersion) {
        this.serverVersion = serverVersion;
    }

    /**
     * Holds the ClassLoader used to retrieve the WebApp JNDI Context and the
     * JOnAS Webapp ClasLoader
     * @author Guillaume Sauthier
     */
    public class WebLoaderHolder {

        /**
         * Web ClassLoader used by JOnAS.
         */
        private URLClassLoader jonasWebLoader;

        /**
         * Web ClassLoader used to retrieve the JNDI Context of the webapp.
         */
        private ClassLoader envWebLoader;

        /**
         * Constructs a WebLoaderHolder with jonas Loader and en Loader.
         * @param jonas JOnAS Webapp Loader
         * @param env Environnement ClassLoader
         */
        public WebLoaderHolder(final URLClassLoader jonas, final ClassLoader env) {
            jonasWebLoader = jonas;
            envWebLoader = env;
        }

        /**
         * @return Returns the jonasWebLoader.
         */
        public URLClassLoader getJonasWebLoader() {
            return jonasWebLoader;
        }

        /**
         * @return Returns the envWebLoader.
         */
        public ClassLoader getEnvWebLoader() {
            return envWebLoader;
        }

        /**
         * @param envWebLoader The envWebLoader to set.
         */
        public void setEnvWebLoader(ClassLoader envWebLoader) {
            this.envWebLoader = envWebLoader;
        }

        /**
         * @param jonasWebLoader The jonasWebLoader to set.
         */
        public void setJonasWebLoader(URLClassLoader jonasWebLoader) {
            this.jonasWebLoader = jonasWebLoader;
        }
    }

}
