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

package org.ow2.jonas.web.base;

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.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import javax.management.MalformedObjectNameException;
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 javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceContextType;

import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
import org.ow2.easybeans.deployment.InjectionHolder;
import org.ow2.easybeans.deployment.api.EZBInjectionHolder;
import org.ow2.easybeans.persistence.api.EZBPersistenceUnitManager;
import org.ow2.easybeans.resolver.api.EZBJNDIResolver;
import org.ow2.easybeans.resolver.api.EZBJNDIResolverException;
import org.ow2.jonas.deployment.api.IEJBLocalRefDesc;
import org.ow2.jonas.deployment.api.IEJBRefDesc;
import org.ow2.jonas.deployment.api.IEnvEntryDesc;
import org.ow2.jonas.deployment.api.IMessageDestinationRefDesc;
import org.ow2.jonas.deployment.api.IResourceEnvRefDesc;
import org.ow2.jonas.deployment.api.IResourceRefDesc;
import org.ow2.jonas.deployment.api.IServiceRefDesc;
import org.ow2.jonas.deployment.common.DeploymentDescException;
import org.ow2.jonas.deployment.web.WebContainerDeploymentDesc;
import org.ow2.jonas.deployment.web.lib.WarDeployableMetadataFactoryHolder;
import org.ow2.jonas.deployment.web.lib.WebDeploymentDescManager;
import org.ow2.jonas.ear.EarServiceException;
import org.ow2.jonas.jmx.JmxService;
import org.ow2.jonas.lib.bootstrap.JProp;
import org.ow2.jonas.lib.bootstrap.LoaderManager;
import org.ow2.jonas.lib.loader.SimpleWebappClassLoader;
import org.ow2.jonas.lib.loader.WebappClassLoader;
import org.ow2.jonas.lib.management.javaee.J2eeObjectName;
import org.ow2.jonas.lib.naming.ComponentContext;
import org.ow2.jonas.lib.security.PermissionManagerException;
import org.ow2.jonas.lib.service.AbsServiceImpl;
import org.ow2.jonas.lib.util.JonasObjectName;
import org.ow2.jonas.lib.util.Log;
import org.ow2.jonas.lib.wsgen.WsGenChecker;
import org.ow2.jonas.naming.JComponentContextFactory;
import org.ow2.jonas.naming.JNamingManager;
import org.ow2.jonas.service.ServiceException;
import org.ow2.jonas.versioning.VersioningService;
import org.ow2.jonas.web.JWebContainerService;
import org.ow2.jonas.web.JWebContainerServiceException;
import org.ow2.jonas.web.base.lib.JarTools;
import org.ow2.jonas.web.base.lib.PermissionManager;
import org.ow2.jonas.ws.WebServicesService;
import org.ow2.util.ee.deploy.api.deployable.EARDeployable;
import org.ow2.util.ee.deploy.api.deployer.IDeployerManager;
import org.ow2.util.ee.metadata.common.api.struct.IJEjbEJB;
import org.ow2.util.ee.metadata.common.api.struct.IJavaxPersistenceContext;
import org.ow2.util.ee.metadata.common.api.struct.IJavaxPersistenceUnit;
import org.ow2.util.ee.metadata.war.api.IWarDeployableMetadataFactory;
import org.ow2.util.ee.metadata.war.impl.enc.EZBENCBindingHolder;
import org.ow2.util.ee.metadata.war.impl.enc.IBinding;
import org.ow2.util.file.FileUtils;
import org.ow2.util.file.FileUtilsException;
import org.ow2.util.url.URLUtils;

/**
 * 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)
 * @author S. Ali Tokmen (versioning)
 */
public abstract class BaseWebContainerService extends AbsServiceImpl implements JWebContainerService,
        BaseWebContainerServiceMBean {

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

    /**
     * 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";

    /**
     * Length of the extension .war.
     */
    private static final int WAR_EXTENSION_LENGTH = ".war".length();

    /**
     * Logger for this service.
     */
    private static Logger logger = Log.getLogger(Log.JONAS_WEB_PREFIX);

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

    /**
     * War deployer.
     */
    private WARDeployer warDeployer = null;

    /**
     * Reference on the ComponentContextFactory.
     */
    private JComponentContextFactory contextFactory;

    /**
     * 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();

    /**
     * JMX Service.
     */
    protected JmxService jmxService = null;

    /**
     * War deployable metadata factory service.
     */
    private IWarDeployableMetadataFactory warDeployableMetadataFactory = null;

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

    /**
     * Name of the server (Tomcat, Jetty, ...).
     */
    private String serverName = null;

    /**
     * Version of the web container.
     */
    private String serverVersion = null;

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

    /**
     * External Classloader.
     */
    private ClassLoader extClassLoader;

    /**
     * DeployerManager service.
     */
    private IDeployerManager deployerManager;

    /**
     * Versioning service.
     */
    private VersioningService versioningService;

    /**
     * Default constructor.
     */
    public BaseWebContainerService() {
        this.warDeployer = new WARDeployer();
    }

    /**
     * @param validate must we parse web.xml files with validation ?
     */
    public void setParsingwithvalidation(final boolean validate) {
        WebDeploymentDescManager.setParsingWithValidation(validate);
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            if (!validate) {
                logger.log(BasicLevel.DEBUG, "Web XML parsing without validation");
            } else {
                logger.log(BasicLevel.DEBUG, "Web XML parsing with validation");
            }
        }
    }

    /**
     * Start the service.
     * @throws ServiceException if the startup failed.
     */
    @Override
    protected void doStart() throws ServiceException {

        // get apps ClassLoader
        try {
            LoaderManager lm = LoaderManager.getInstance();
            extClassLoader = lm.getExternalLoader();
        } 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);
        }

        // Init the deployer
        warDeployer.setWebContainerService(this);

        // Register the deployer
        deployerManager.register(warDeployer);

        registerWebServiceMBean(this, getDomainName());
    }

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

        // Unregister the deployer
        deployerManager.unregister(warDeployer);

        // 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.get(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 ComponentContext(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());
            }
        }

        unregisterWebServiceMBean(getDomainName());

        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(final URL warURL, final String earAppName) throws JWebContainerServiceException {

        String destDir = WORK_WEBAPPS_DIR + File.separator + getJonasServerName() + File.separator;

        // Two cases :
        // war is alone --> unpack it in :
        //   WEBAPPS_DIR/servername/war_dir/
        // war come from an ear --> unpack it in :
        //   WEBAPPS_DIR/servername/ear_AppName/war_dir/
        // where war_dir = lastModifiedFileName(filename_without_extension_dir)

        // location of unpacked War
        URL unpackedWarURL = null;

        String fileName = warURL.getFile();
        File warFile = new File(fileName);

        if (warFile.isFile()) {
            // Note : if the war is alone, earAppName is null.
            if (earAppName != null) {
                destDir += INEAR_WORK_WEBAPPS_DIR_SUFFIX + File.separator + earAppName + File.separator;
            } else {
                destDir += SINGLE_WORK_WEBAPPS_DIR_SUFFIX + File.separator;
            }
            try {
                destDir += FileUtils.lastModifiedFileName(warFile);
            } catch (FileUtilsException e) {
                throw new JWebContainerServiceException("Failed creating the unpackedWarURL", e);
            }

            // 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("Failed getting the unpackedWarURL", 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(final URL warURL, final String earAppName, final 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, extClassLoader);
                }
            } 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(final 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.
     */
    protected void registerWar(final 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).
        //   - earDeployable the EARDeployable of the ear application file.
        //     (optional parameter, same comment as earURL).
        //   - 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;
        EARDeployable earDeployable = null;
        String contextRoot = null;
        String earAppName = null;
        String userURI = null;

        // Injection holder
        EZBInjectionHolder ezbInjectionHolder = null;

        try {
            earURL = (URL) ctx.lookup("earURL");
            earDeployable = (EARDeployable) ctx.lookup("earDeployable");
            contextRoot = (String) ctx.lookup("contextRoot");
        } catch (NamingException e) {
            if (earURL != null || earDeployable != 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 EAR application name
        if (earDeployable != null) {
            earAppName = earDeployable.getModuleName();
        }

        // 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);
        }

        // Get injection holder
        try {
            ezbInjectionHolder = (EZBInjectionHolder) ctx.lookup(EZBInjectionHolder.class.getName());
        } catch (NamingException e) {
            logger.log(BasicLevel.DEBUG, "No injection holder, create a new one");
            ezbInjectionHolder = new InjectionHolder();
        }

        // ... 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(warFile.getCanonicalPath(), j2eeServer, jmxService.getJmxServer());
                wsgc.checkWsGen(getDomainName());
            } catch (Exception e) {
                throw new EarServiceException("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);

        // Only if the WebServicesService is started
        // And in non EAR case
        if (wsService != null && earClassLoader == null) {
            try {
                ComponentContext contctx = null;
                try {
                    contctx = new ComponentContext(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 {
            WebDeploymentDescManager manager = WebDeploymentDescManager.getInstance();
            webDD = manager.getDeploymentDesc(unpackedWarURL, warURL, loaderForCls, earClassLoader);
        } catch (DeploymentDescException 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 ComponentContext(unpackedWarURL.getFile());
            ctxParam.rebind("DeploymentDesc", webDD);
            ctxParam.rebind("warName", unpackedWarURL.getFile());
            if (parentLoader == null) {
                webClassLoader = new SimpleWebappClassLoader(unpackedWarURL, extClassLoader);
            } else {
                webClassLoader = new SimpleWebappClassLoader(unpackedWarURL, parentLoader);
            }
            if (earDeployable != null) {
                ctxParam.rebind("earDeployable", earDeployable);
            }
            ctxParam.rebind("parentCL", webClassLoader);
            // Do the populating of the java:comp/env (ENC) environment.
            setWebEnvironment(ctxParam, ezbInjectionHolder, webClassLoader);
        } 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);

        contextRoot = getContextRoot(contextRoot, earClassLoader, warURL, webDD);
        String versionID = null;
        if (isVersioningEnabled()) {
            if (earURL != null) {
                versionID = versioningService.getVersionID(earDeployable);
            } else {
                versionID = versioningService.getVersionID(warURL);
            }
            // TODO: prefix for deploying MBeans inside this WAR (filters, ...)
        }
        if (versionID == null) {
            userURI = contextRoot;
        } else {
            if (!contextRoot.contains(versionID)) {
                userURI = contextRoot;
                contextRoot += versionID;
            } else {
                userURI = contextRoot.replace(versionID, "");
            }
        }

        // 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 ComponentContext(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);

            ctxParam.rebind("userURI", userURI);
        } 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.add(war);
        warBindings.put(warURL, webClassLoader);

        // Admin code
        registerWarMBean(war, getDomainName(), warURL.getFile());

        // Complete the Deployment of the WebServices
        // Only if the WebServicesService is started
        // And in non EAR case
        if (wsService != null && earClassLoader == null) {
            try {
                ComponentContext contctx = null;
                try {
                    contctx = new ComponentContext(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);
        }
    }

    /**
     * Gets the context root for a WAR.
     * @param contextRoot Current context root, null if none.
     * @param earClassLoader EAR classloader for the WAR, null if none.
     * @param warURL War file's URL.
     * @param webDD Deployment descriptor of the WAR file.
     * @return Context root, checked (always begin with one slash).
     */
    protected String getContextRoot(String contextRoot, final URLClassLoader earClassLoader, final URL warURL,
            final WebContainerDeploymentDesc webDD) {
        // 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 WAR file without extension.
        if (earClassLoader == null && contextRoot == null) {
            String cRoot = webDD.getContextRoot();
            if (cRoot == null) {
                String file = new File(warURL.getFile()).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);
        }
        return contextRoot;
    }

    /**
     * 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 registerWar(final 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 ComponentContext(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.
     * @param ezbInjectionHolder the easybeans injection handler.
     * @param webAppClassLoader the classloader of the web application.
     * @throws JWebContainerServiceException if the populating of the
     *         environment failed.
     */
    protected void setWebEnvironment(final Context ctxParam, final EZBInjectionHolder ezbInjectionHolder,
            final ClassLoader webAppClassLoader) throws JWebContainerServiceException {

        WebContainerDeploymentDesc dd = null;
        String warName = null;
        ClassLoader parentClassLoader = null;
        EARDeployable earDeployable = 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");
            earDeployable = (EARDeployable) ctxParam.lookup("earDeployable");
        } catch (NamingException e) {
            // Ignore if earDeployable not found, it's optional
            if (earDeployable != null) {
                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 = contextFactory.createComponentContext(warName);
            naming.setComponentContext(javaCtx, parentClassLoader);
            Context envCtx = javaCtx.createSubcontext("comp/env");

            // War Environment entries
            IEnvEntryDesc[] 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
            IResourceRefDesc[] 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.ow2.jonas.lib.naming.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
            IResourceEnvRefDesc[] 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
            IEJBRefDesc[] 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.
            IEJBLocalRefDesc[] 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
            IMessageDestinationRefDesc[] 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
            // Only done when the WebServiceService is active
            if (wsService != null) {
                IServiceRefDesc[] serviceRefs = dd.getServiceRefDesc();
                for (int i = 0; i < serviceRefs.length; i++) {

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

            // Gets the bindings
            EZBENCBindingHolder encBindingHolder = dd.getENCBindingHolder();

            // Persistence context
            EZBPersistenceUnitManager persistenceUnitManager = ezbInjectionHolder.getPersistenceUnitManager();
            for (IBinding<IJavaxPersistenceContext> binding : encBindingHolder.getPersistenceContextBindings()) {
                String encName = binding.getName();
                String unitName = binding.getValue().getUnitName();
                PersistenceContextType type = binding.getValue().getType();
                EntityManager em = persistenceUnitManager.getEntityManager(unitName, type);

                envCtx.rebind(encName, em);
                if (logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG, "Adding persistence-context 'java:comp/env/" + encName + "'");
                }
            }

            // Persistence unit
            for (IBinding<IJavaxPersistenceUnit> binding : encBindingHolder.getPersistenceUnitBindings()) {
                String encName = binding.getName();
                String unitName = binding.getValue().getUnitName();
                EntityManagerFactory emf = persistenceUnitManager.getEntityManagerFactory(unitName);

                envCtx.rebind(encName, emf);
                if (logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG, "Adding persistence-unit 'java:comp/env/" + encName + "'");
                }
            }

            // EJBs
            EZBJNDIResolver jndiResolver = ezbInjectionHolder.getJNDIResolver();
            if (jndiResolver != null) {
                for (IBinding<IJEjbEJB> binding : encBindingHolder.getEJBBindings()) {
                    String encName = binding.getName();
                    IJEjbEJB jEjbEJB = binding.getValue();
                    String interfaceName = jEjbEJB.getBeanInterface();
                    String beanName = jEjbEJB.getBeanName();

                    String jndiName;
                    try {
                        jndiName = jndiResolver.getUniqueName(interfaceName, beanName);

                        // Bind a link to this JNDI name
                        envCtx.rebind(encName, new LinkRef(jndiName));
                        if (logger.isLoggable(BasicLevel.DEBUG)) {
                            logger.log(BasicLevel.DEBUG, "Adding ejb 'java:comp/env/" + encName + "' from JNDIName '" + jndiName
                                    + "'.");
                        }

                    } catch (EZBJNDIResolverException e) {
                        logger.log(BasicLevel.ERROR, "Cannot get JNDI name for enc name '" + encName + "'", e);

                    }

                }
            }



            // JNDI lookup prefix
            if (earDeployable != null && isVersioningEnabled()) {
                envCtx.rebind("JNDILookupPrefix", versioningService.getPrefix(earDeployable));
                if (logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG, "Adding JNDI-lookup-prefix 'java:comp/env/JNDILookupPrefix'");
                }
            }
        } 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.
     */
    protected void unRegisterWar(final 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.remove(war);

        // Admin code
        unregisterWarMBean(getDomainName(), fileName);

        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 unRegisterWar(final 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 ComponentContext(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(final 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.
        //    - earDeployable the EARDeployable 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;
        EARDeployable earDeployable = null;
        ClassLoader parentClassLoader = null;
        ClassLoader earClassLoader = null;
        URL[] altDDs = null;
        String[] contextRoots = null;
        EZBInjectionHolder ejbInjectionHolder = null;
        try {
            urls = (URL[]) ctx.lookup("urls");
            earURL = (URL) ctx.lookup("earURL");
            earDeployable = (EARDeployable) ctx.lookup("earDeployable");
            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);
        }

        // Get Injection holder
        try {
            ejbInjectionHolder = (EZBInjectionHolder) ctx.lookup(EZBInjectionHolder.class.getName());
        } catch (NamingException e) {
            logger.log(BasicLevel.DEBUG, "No persistence unit manager");
        }


        //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 = URLUtils.urlToFile(urls[i]).getPath();
            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 ComponentContext(fileName);
                contctx.rebind("warURL", urls[i]);
                contctx.rebind("parentClassLoader", parentClassLoader);
                contctx.rebind("earClassLoader", earClassLoader);
                contctx.rebind("earDeployable", earDeployable);
                contctx.rebind("earURL", earURL);
                if (altDDs[i] != null) {
                    contctx.rebind("altDD", altDDs[i]);
                }
                if (contextRoots[i] != null) {
                    contctx.rebind("contextRoot", contextRoots[i]);
                }
                // Injection holder
                if (ejbInjectionHolder != null) {
                    contctx.rebind(EZBInjectionHolder.class.getName(), ejbInjectionHolder);
                }
                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.
                        ComponentContext context = new ComponentContext(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(final 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.
                ComponentContext context = new ComponentContext(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(final URL url) {
        Iterator wars = warDeployed.iterator();
        while (wars.hasNext()) {
            War war = (War) wars.next();
            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.
     */
    protected List getWar(final String pContext) {
        List checkDeployed = new ArrayList();
        Iterator wars = warDeployed.iterator();
        while (wars.hasNext()) {
            War war = (War) wars.next();
            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(final ClassLoader earClassLoader) {
        WebDeploymentDescManager.getInstance().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.
     */
    protected URL checkWarFile(final String fileName) throws JWebContainerServiceException {

        File f = null;
        try {
            f = new File(fileName).getCanonicalFile();
            if (!f.exists()) {
                boolean found = false;
            }
        } 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.
     */
    protected URL checkWarDeployed(final String fileName) throws JWebContainerServiceException {

        URL url = null;
        try {
            Iterator wars = warDeployed.iterator();
            while (wars.hasNext()) {
                War war = (War) wars.next();
                url = (new File(fileName).getCanonicalFile()).toURL();
                URL deployedWarURL = war.getWarURL();
                String deployedWarPath = deployedWarURL.toString();
                // Delete the char '/' at the end of the URL if present
                if (deployedWarPath.endsWith(File.separator)) {
                    deployedWarPath = deployedWarPath.substring(0, deployedWarPath.length() - 1);
                }
                if (deployedWarPath.equals(url.toString())) {
                    return deployedWarURL;
                }
            }

            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());
    }

    /**
     * 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(final 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;
            }
        } 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<String> getDeployedWars() {
        List<String>  al = new ArrayList<String>();
        for (Iterator wars = warDeployed.iterator(); wars.hasNext();) {
            War war = (War) wars.next();
            URL warURL = war.getWarURL();
            al.add(warURL.getFile());
        }
        return al;
    }

    /**
     * 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;
    }

    /**
     * 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 JNamingManager getNaming() {
        return naming;
    }

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

    /**
     * @param serverVersion The serverVersion to set.
     */
    protected void setServerVersion(final 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(final ClassLoader envWebLoader) {
            this.envWebLoader = envWebLoader;
        }

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

    /**
     * @return the {@link JComponentContextFactory}.
     */
    protected JComponentContextFactory getContextFactory() {
        return contextFactory;
    }

    // Admin code (JMX based)
    // ---------------------
    /**
     * Register WEB Container Service MBean.
     * @param service web container service to manage
     * @param domainName domain name
     */
    protected void registerWebServiceMBean(final Object service, final String domainName) {
        ObjectName on = JonasObjectName.webContainerService(domainName);
        jmxService.registerMBean(service, on);
    }

    /**
     * Unregister WEB Container Service MBean.
     * @param domainName domain name
     */
    protected void unregisterWebServiceMBean(final String domainName) {
        ObjectName on = JonasObjectName.webContainerService(domainName);
        jmxService.unregisterMBean(on);
    }

    /**
     * Register War MBean.
     * @param war War instance to manage
     * @param domainName domain name
     * @param fileName the war file name
     */
    protected void registerWarMBean(final War war, final String domainName, final String fileName) {
        //ObjectName on = null;
        try {
            ObjectName on = JonasObjectName.war(domainName, fileName);
            jmxService.registerMBean(war, on);
        } catch (MalformedObjectNameException e) {
            logger.log(BasicLevel.WARN, "Could not register War MBean", e);
        }
    }

    /**
     * Unegister War MBean.
     * @param domainName domain name
     * @param fileName the war file name
     */
    protected void unregisterWarMBean(final String domainName, final String fileName) {
        //ObjectName on = null;
        try {
            ObjectName on = JonasObjectName.war(domainName, fileName);
            jmxService.unregisterMBean(on);
        } catch (MalformedObjectNameException e) {
            logger.log(BasicLevel.WARN, "Could not unregister War MBean", e);
        }
    }

    /**
     * @param jmxService the jmxService to set
     */
    public void setJmxService(final JmxService jmxService) {
        this.jmxService = jmxService;
    }

    /**
     * @param wsService the wsService to set
     */
    public void setWsService(final WebServicesService wsService) {
        this.wsService = wsService;
    }

    /**
     * @param naming the naming to set
     */
    public void setNaming(final JNamingManager naming) {
        this.naming = naming;
    }

    /**
     * @param contextFactory the contextFactory to set
     */
    public void setContextFactory(final JComponentContextFactory contextFactory) {
        this.contextFactory = contextFactory;
    }

    /**
     * @param deployerManager the {@link IDeployerManager} to use.
     */
    public void setDeployerManager(final IDeployerManager deployerManager) {
        this.deployerManager = deployerManager;
    }

    /**
     * @return Associates an URL of an unpacked WAR file to its classloader.
     */
    protected Hashtable getWarLoaders() {
        return warLoaders;
    }

    /**
     * @return Associates an URL of a deployed WAR file to its classloader.
     */
    protected Hashtable getWarBindings() {
        return warBindings;
    }

    /**
     * @return List of the war deployed by the Web container Service.
     */
    protected List getWarDeployed() {
        return warDeployed;
    }

    /**
     * @return Reference to the WebServices service.
     */
    protected WebServicesService getWsService() {
        return wsService;
    }

    /**
     * @return Application Classloader.
     */
    protected ClassLoader getAppsClassLoader() {
        return extClassLoader;
    }

    /**
     * @param versioningService  The versioning service to set.
     */
    public void setVersioningService(final VersioningService versioningService) {
        this.versioningService = versioningService;
    }

    /**
     * Sets the versioning service to null.
     */
    public void unsetVersioningService() {
        this.versioningService = null;
    }

    /**
     * @return The versioning service.
     */
    public VersioningService getVersioningService() {
        return this.versioningService;
    }

    /**
     * @return Whether versioning is enabled.
     */
    public boolean isVersioningEnabled() {
        return (versioningService != null && versioningService.isVersioningEnabled());
    }

    /**
     * @param warDeployableMetadataFactory the deployable metadata factory service
     */
    public void setWarDeployableMetadataFactory(final IWarDeployableMetadataFactory warDeployableMetadataFactory) {
        WarDeployableMetadataFactoryHolder.setWarDeployableMetadataFactory(warDeployableMetadataFactory);
    }
}
