/*
 * The contents of this file are subject to the terms 
 * of the Common Development and Distribution License 
 * (the License).  You may not use this file except in
 * compliance with the License.
 * 
 * You can obtain a copy of the license at 
 * https://glassfish.dev.java.net/public/CDDLv1.0.html or
 * glassfish/bootstrap/legal/CDDLv1.0.txt.
 * See the License for the specific language governing 
 * permissions and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL 
 * Header Notice in each file and include the License file 
 * at glassfish/bootstrap/legal/CDDLv1.0.txt.  
 * If applicable, add the following below the CDDL Header, 
 * with the fields enclosed by brackets [] replaced by
 * you own identifying information: 
 * "Portions Copyrighted [year] [name of copyright owner]"
 * 
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 */

package com.sun.enterprise.web;

import com.sun.enterprise.util.OS;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.text.MessageFormat;
import java.util.Iterator;
import java.util.HashMap;
//HERCULES:add
import java.util.Hashtable;
import java.util.ArrayList;
//end HERCULES:add
import java.util.List;
import java.util.ListIterator;
import java.util.logging.Logger;
import java.util.logging.Level;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.Vector;
import javax.naming.NamingException;

import org.apache.catalina.startup.Embedded;
import org.apache.catalina.Container;
import org.apache.catalina.Context;
import org.apache.catalina.Deployer;
import org.apache.catalina.Engine;
import org.apache.catalina.Loader;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.Manager;
import org.apache.catalina.Valve;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardEngine;
import org.apache.catalina.core.StandardHost;
import org.apache.catalina.deploy.ErrorPage;
import org.apache.catalina.loader.WebappLoader;
import org.apache.catalina.session.FileStore;
import org.apache.catalina.session.PersistentManager;
import org.apache.catalina.session.StandardManager;
import org.apache.catalina.util.LifecycleSupport;
import org.apache.catalina.util.ServerInfo;
import org.apache.catalina.startup.TldConfig;
import org.apache.catalina.startup.DigesterFactory;
import org.apache.catalina.valves.RemoteAddrValve;
import org.apache.catalina.valves.RemoteHostValve;
import org.apache.jasper.compiler.TldLocationsCache;
import org.apache.jasper.xmlparser.ParserUtils;
import org.apache.coyote.tomcat5.CoyoteRequest;
import com.sun.enterprise.config.ConfigBean;
import com.sun.enterprise.config.ConfigContext;
import com.sun.enterprise.config.ConfigBeansFactory;
import com.sun.enterprise.config.ConfigException;
import com.sun.enterprise.config.serverbeans.ApplicationHelper;
import com.sun.enterprise.config.serverbeans.ServerBeansFactory;
import com.sun.enterprise.config.serverbeans.Applications;
import com.sun.enterprise.config.serverbeans.J2eeApplication;
import com.sun.enterprise.config.serverbeans.LogService;
import com.sun.enterprise.config.serverbeans.Config;
import com.sun.enterprise.config.serverbeans.DasConfig;
import com.sun.enterprise.config.serverbeans.Server;
import com.sun.enterprise.config.serverbeans.HttpService;
import com.sun.enterprise.config.serverbeans.HttpProtocol;
import com.sun.enterprise.config.serverbeans.ElementProperty;
import com.sun.enterprise.instance.InstanceEnvironment;
import com.sun.enterprise.server.ApplicationRegistry;
import com.sun.enterprise.server.StandaloneWebModulesManager;
import com.sun.enterprise.server.ServerContext;
import com.sun.enterprise.server.pluggable.WebContainerFeatureFactory;
import com.sun.enterprise.deployment.runtime.web.SunWebApp;
import com.sun.enterprise.deployment.runtime.web.CookieProperties;
import com.sun.enterprise.deployment.runtime.web.ManagerProperties;
import com.sun.enterprise.deployment.runtime.web.SessionConfig;
import com.sun.enterprise.deployment.runtime.web.SessionManager;
import com.sun.enterprise.deployment.runtime.web.SessionProperties;
import com.sun.enterprise.deployment.runtime.web.StoreProperties;
import com.sun.enterprise.deployment.runtime.web.WebProperty;
import com.sun.enterprise.deployment.Application;
import com.sun.enterprise.deployment.util.WebValidatorWithoutCL;
import com.sun.enterprise.deployment.util.WebBundleVisitor;
import com.sun.enterprise.management.util.J2EEModuleUtil;
import com.sun.enterprise.util.io.FileUtils;
import com.sun.enterprise.util.logging.IASLevel;
import com.sun.enterprise.util.StringUtils;
import com.sun.enterprise.web.logger.IASLogger;
import com.sun.enterprise.web.session.PersistenceType;
import com.sun.enterprise.web.session.SessionCookieConfig;
import com.sun.enterprise.security.web.SingleSignOn;

import com.sun.enterprise.security.SecurityUtil;
import com.sun.enterprise.deployment.interfaces.SecurityRoleMapper;
import com.sun.enterprise.deployment.interfaces.SecurityRoleMapperFactory;

import com.sun.enterprise.deployment.Application;
import com.sun.enterprise.deployment.WebBundleDescriptor;
import com.sun.enterprise.deployment.WebServicesDescriptor;
import com.sun.enterprise.deployment.WebServiceEndpoint;
import com.sun.enterprise.instance.WebModulesManager;
import com.sun.enterprise.instance.AppsManager;
import com.sun.enterprise.Switch;
import com.sun.appserv.server.ServerLifecycleException;
import com.sun.appserv.server.util.ASClassLoaderUtil;
import com.sun.appserv.server.util.Version;
import com.sun.logging.LogDomains;

import com.sun.web.security.WebSecurityManager;
import com.sun.web.security.WebSecurityManagerFactory;
import com.sun.web.security.RealmAdapter;

// monitoring imports
import java.util.HashSet;
import javax.management.j2ee.statistics.Stats;
import com.sun.enterprise.deployment.WebComponentDescriptor;
import com.sun.enterprise.admin.monitor.stats.ServletStats;
import com.sun.enterprise.web.stats.ServletStatsImpl;
import com.sun.enterprise.web.monitor.PwcServletStats;
import com.sun.enterprise.web.monitor.impl.PwcServletStatsImpl;
import com.sun.enterprise.admin.monitor.stats.WebModuleStats;
import com.sun.enterprise.web.stats.WebModuleStatsImpl;
import com.sun.enterprise.web.monitor.PwcWebModuleStats;
import com.sun.enterprise.web.monitor.impl.PwcWebModuleStatsImpl;
import com.sun.enterprise.admin.monitor.registry.MonitoringRegistry;
import com.sun.enterprise.admin.monitor.registry.MonitoringRegistrationException;
import com.sun.enterprise.admin.monitor.registry.MonitoringLevel;
import com.sun.enterprise.admin.monitor.registry.MonitoringLevelListener;
import com.sun.enterprise.admin.monitor.registry.MonitoredObjectType;
import com.sun.enterprise.config.serverbeans.MonitoringService;
import com.sun.enterprise.config.serverbeans.ModuleMonitoringLevels;

//HERCULES:add
//admin event imports
import com.sun.enterprise.admin.event.ApplicationDeployEvent;
import com.sun.enterprise.admin.event.ApplicationDeployEventListener;
import com.sun.enterprise.admin.event.ModuleDeployEvent;
import com.sun.enterprise.admin.event.ModuleDeployEventListener;
import com.sun.enterprise.admin.event.AdminEventListenerRegistry;
//end HERCULES:add

// Begin EE: 4927099 load only associated applications
import com.sun.enterprise.config.serverbeans.Domain;
import com.sun.enterprise.config.serverbeans.Servers;
import com.sun.enterprise.config.serverbeans.ApplicationRef;
// End EE: 4927099 load only associated applications

/**
 * Web container implementation for the Sun ONE Application Server.
 *
 * This class is intended for use in versions of the application server
 * that *do not* have web-core as well as in versions that do. NSAPI
 * specific functionality is implemented the NSAPIWebContainer subclass
 * in the <code>j2ee-plugin</code> component.
 */
public class WebContainer
    implements Lifecycle,
               ApplicationDeployEventListener,
               ModuleDeployEventListener,
               MonitoringLevelListener {

    // ------------------------------------------------------------ Constants
    
    public static final String DISABLED = "disabled";
    public static final String OFF = "off";
    public static final String ON = "on";
    public static final String STATE = "state";
    public static final String SSO_ENABLED="sso-enabled";
    public static final String SSO_MAX_IDLE="sso-max-inactive-seconds";
    public static final String SSO_REAP_INTERVAL="sso-reap-interval-seconds";
    public static final String ADMIN_VS = "__asadmin";    
    public static final String DISPATCHER_MAX_DEPTH="dispatcher-max-depth";

    static final int DEFAULT_REAP_INTERVAL = 60;   // 1 minute

    public static final String JWS_APPCLIENT_EAR_NAME = "__JWSappclients";
    public static final String JWS_APPCLIENT_WAR_NAME = "sys";
    private static final String JWS_APPCLIENT_MODULE_NAME = JWS_APPCLIENT_EAR_NAME + ":" + JWS_APPCLIENT_WAR_NAME + ".war";

    // ----------------------------------------------------- Instance Variables

    /**
     * The embedded Catalina object.
     */
    protected EmbeddedWebContainer _embedded = null;

    /**
     * The parent/top-level container in <code>_embedded</code> for virtual
     * servers.
     */

    /**
     * The server context under which this container was created.
     */
    protected ServerContext _serverContext = null;

    /**
     * The config context under which this container was created.
     */
    protected ConfigContext _configContext = null;

    protected Domain domain = null;
    protected InstanceEnvironment instance = null;
    protected WebModulesManager webModulesManager = null;
    protected AppsManager appsManager = null;

    /**
     * The schema2beans object that represents the root node of server.xml.
     */
    private Server _serverBean = null;

    /**
     * The id of this web container object.
     */
    private String _id = null;

    /**
     * The logger to use for logging ALL web container related messages.
     */
    protected static Logger _logger = null;

    /**
     * The resource bundle containing the message strings for _logger.
     */
    protected static ResourceBundle _rb = null;

    /*
     * The current web container monitoring level
     */
    protected static MonitoringLevel monitoringLevel;

    /**
     * The current level of logging verbosity for this object.
     */
    protected Level _logLevel = null;

    /**
     * Controls the verbosity of the web container subsystem's debug messages.
     *
     * This value is non-zero only when the iAS level is one of FINE, FINER
     * or FINEST.
     */
    protected int _debug = 0;

    /**
     * Top-level directory for files generated (compiled JSPs) by
     *  standalone web modules.
     */
    private String _modulesWorkRoot = null;

    // START S1AS 6178005
    /**
     * Top-level directory where ejb stubs of standalone web modules are stored
     */
    private String modulesStubRoot = null;
    // END S1AS 6178005

    /**
     * Absolute path for location where all the deployed
     * standalone modules are stored for this Server Instance.
     */
    protected String _modulesRoot = null;

    /**
     * Top-level directory for files generated by application web modules.
     */
    private String _appsWorkRoot = null;

    // START S1AS 6178005
    /**
     * Top-level directory where ejb stubs for applications are stored.
     */
    private String appsStubRoot = null;
    // END S1AS 6178005

    /**
     * Indicates whether dynamic reloading is enabled (as specified by
     * the dynamic-reload-enabled attribute of <applications> in server.xml)
     */
    private boolean _reloadingEnabled = false;
    
    /**
     * The number of seconds between checks for modified classes (if 
     * dynamic reloading is enabled).
     *
     * This value is specified by the reload-poll-interval attribute of
     * <applications> in server.xml.
     */
    private int _pollInterval = 2;

    /**
     * Adds/removes standalone web modules to the reload monitor thread
     * (when dynamic reloading is enabled in server.xml).
     */
    private StandaloneWebModulesManager _reloadManager = null;

    /**
     * The lifecycle event support for this component.
     */
    private LifecycleSupport _lifecycle = new LifecycleSupport(this);

    /**
     * Has this component been started yet?
     */
    protected boolean _started = false;
        
    /**
     * The WebSecurityManagerFactory used for generating web permission
     */
    private WebSecurityManagerFactory webSecurityManagerFactory 
           = WebSecurityManagerFactory.getInstance(); 

    private EjbWebServiceRegistryListener ejbWebServiceRegistryListener;

    
    private static final String DOL_DEPLOYMENT =
                                   "com.sun.enterprise.web.deployment.backend";
    
    /**
     * Are we using Tomcat deployment backend or DOL?
     */
    protected static boolean useDOLforDeployment = true;

    /**
     * The instance classpath, which is composed of the pathnames of
     * domain_root/lib/classes and domain_root/lib/[*.jar|*.zip] (in this
     * order), separated by the path-separator character.
     */
    private String instanceClassPath;

    
    static{     
        if (System.getProperty(DOL_DEPLOYMENT) != null){
            useDOLforDeployment = Boolean.valueOf(
                            System.getProperty(DOL_DEPLOYMENT)).booleanValue();
        }               
    }
    
    /**
     * The current <code>WebContainer</code> instance used (single).
     */
    protected static WebContainer webContainer;
    // ------------------------------------------------------------ Constructor

    /**
     * This creates the embedded Catalina/Jasper container and sets the config
     * properties on the container.
     */
    protected WebContainer(String id, ServerContext context) {

        _id = id;
        _serverContext = context;

        _configContext = _serverContext.getConfigContext();

        String rootDir = _serverContext.getInstallRoot();
        String name = _serverContext.getInstanceName();
        instance = new InstanceEnvironment(rootDir, name);
        _modulesWorkRoot = instance.getWebModuleCompileJspPath();
        _appsWorkRoot = instance.getApplicationCompileJspPath();
        _modulesRoot = instance.getModuleRepositoryPath();

        instanceClassPath = getInstanceClassPath(instance);

        ejbWebServiceRegistryListener = new EjbWebServiceRegistryListener(this);

        // START S1AS 6178005
        modulesStubRoot = instance.getModuleStubPath();
        appsStubRoot = instance.getApplicationStubPath();
        // END S1AS 6178005

        // XXX logger initialization should probably be done in
        //     in LogServiceLifecycle.onInitialization()
        if (_logger == null) {
            _logger = LogDomains.getLogger(LogDomains.WEB_LOGGER);
            _rb = _logger.getResourceBundle();
        }

        try {
            webModulesManager = new WebModulesManager(instance);
            appsManager = new AppsManager(instance);
        } catch (ConfigException cx) {
            _logger.log(Level.WARNING,
                "Error in creating web modules manager: ", cx);
        }

        setNoTldScan();

        LogService logService = null;

        try {
            domain = (Domain) _configContext.getRootConfigBean();

            _serverBean = ServerBeansFactory.getServerBean(_configContext);

            getDynamicReloadingSettings(ServerBeansFactory.getDasConfigBean(_configContext));

            Config cfg = ServerBeansFactory.getConfigBean(_configContext);
            logService = cfg.getLogService();
            initLogLevel(logService);
            initMonitoringLevel(cfg.getMonitoringService());

            ElementProperty maxDepth
                = ServerBeansFactory.getWebContainerBean(_configContext).getElementPropertyByName(DISPATCHER_MAX_DEPTH);
            if (maxDepth != null && maxDepth.getValue() != null) {

                int depth = -1;
                try {
                    depth = Integer.parseInt(maxDepth.getValue());
                } catch (Exception e) {}

                if (depth > 0) {
                    CoyoteRequest.setMaxDispatchDepth(depth);
                    if (_logger.isLoggable(Level.FINE)) {
                        _logger.fine("Maximum depth for nested request "
                                     + "dispatches set to "
                                     + maxDepth.getValue());
                    }
                }
            }

        } catch (ConfigException e) {
            _logger.log(Level.SEVERE, "webcontainer.configError", e);
        }

        String logServiceFile = null;
        if (logService != null) {
            logServiceFile = logService.getFile();
        }
        _embedded = new EmbeddedWebContainer(_logger, _serverContext, this,
                                             logServiceFile);
        _embedded.setUseNaming(false);
        if (_debug > 1)
            _embedded.setDebug(_debug);
        _embedded.setLogger(new IASLogger(_logger));

        DigesterFactory.setSchemaResourcePrefix("/schemas/");
        DigesterFactory.setDtdResourcePrefix("/dtds/");
        ParserUtils.setSchemaResourcePrefix("/schemas/");
        ParserUtils.setDtdResourcePrefix("/dtds/");

        /*
         * Set the server name and version.
         * Allow customers to override this information by specifying
         * product.name system property. For example, some customers prefer
         * not to disclose the product name and version for security
         * reasons, in which case they would set the value of the 
         * product.name system property to the empty string.
         */
        String serverInfo = System.getProperty("product.name");
        if (serverInfo != null) {
            ServerInfo.setServerInfo(serverInfo);
        } else {
            ServerInfo.setServerInfo(Version.getVersion());
        }
  
        //HERCULES:add
        //added for internal monitoring
        WebDebugMonitor debugMonitor = new WebDebugMonitor();
        HashMap monitorMap = debugMonitor.getDebugMonitoringDetails();
        debugMonitoring = ((Boolean) monitorMap.get("debugMonitoring")).booleanValue();
        debugMonitoringPeriodMS = ((Long) monitorMap.get(
                                    "debugMonitoringPeriodMS")).longValue();

        if (debugMonitoring) {
            _timer.schedule(new DebugMonitor(_embedded), 0L,
                                             debugMonitoringPeriodMS);
        }
        //added for internal monitoring
        //END HERCULES:add      
    }
    
    //HERCULES:add
    //added for monitoring
    private static boolean debugMonitoring=false; 
    private static long debugMonitoringPeriodMS = 30000L;
    private static WebContainerTimer _timer = new WebContainerTimer();
    //added for monitoring
    //END HERCULES:add


    // ------------------------------------------------------------ Properties

    /**
     * Return the web container identifier.
     */
    public String getID() {
        return _id;
    }
    
     // --------------------------------------------------------- HADB Health Status 
     
    private HealthChecker _healthChecker = null;
    public HealthChecker getHealthChecker() {
        return _healthChecker;
    }
 
    private void initHealthChecker() {
        //added the pluggable interface way of getting the health checker
        WebContainerFeatureFactory webContainerFeatureFactory =
            _serverContext.getPluggableFeatureFactory().getWebContainerFeatureFactory();
        HealthChecker healthChecker = 
            webContainerFeatureFactory.getHADBHealthChecker(this);
        if(_logger.isLoggable(Level.FINEST)) {
            _logger.finest("WebContainer>>initHealthChecker - healthChecker = " + healthChecker);
        }
        if(healthChecker != null) {
            _healthChecker = healthChecker;
            try {
                _healthChecker.start();
            } catch (LifecycleException ex) {}                
        }
     }
     
     private void stopHealthChecker() {
         if(_healthChecker != null) {
             try {
                 _healthChecker.stop();
             } catch (LifecycleException ex) {}
             _healthChecker = null;
         }
     }    
     
     // --------------------------------------------------------- end HADB Health Status    
    
    
    // ------------------------------------------------ Admin Event Processing
    
    private WebContainerAdminEventProcessor _adminEventProcessor = null;
    
    //HERCULES:add
    public void registerAdminEvents() {    

        AdminEventListenerRegistry.addApplicationDeployEventListener(this);
        AdminEventListenerRegistry.addModuleDeployEventListener(this);
        WebContainerFeatureFactory webContainerFeatureFactory =
            _serverContext.getPluggableFeatureFactory().getWebContainerFeatureFactory();                   
	_adminEventProcessor = 
            webContainerFeatureFactory.getWebContainerAdminEventProcessor();
        _adminEventProcessor.init(_embedded);
    }
    
    public void unregisterAdminEvents() {
        AdminEventListenerRegistry.removeEventListener(this);
        _adminEventProcessor = null;
    }    

    public void applicationDeployed(ApplicationDeployEvent deployEvent) {
        _adminEventProcessor.applicationDeployed(deployEvent);
    }
    
    public void applicationUndeployed(ApplicationDeployEvent deployEvent) {
        _adminEventProcessor.applicationUndeployed(deployEvent);
    }  
    
    public void applicationRedeployed(ApplicationDeployEvent deployEvent) {
        _adminEventProcessor.applicationRedeployed(deployEvent);
    } 
    
    public void applicationEnabled(ApplicationDeployEvent deployEvent) {
    } 
    
    public void applicationDisabled(ApplicationDeployEvent deployEvent) {
    }   
    
    public void moduleDeployed(ModuleDeployEvent deployEvent) {
        _adminEventProcessor.moduleDeployed(deployEvent);
    }
    
    public void moduleUndeployed(ModuleDeployEvent deployEvent) {
        _adminEventProcessor.moduleUndeployed(deployEvent);
    }  
    
    public void moduleRedeployed(ModuleDeployEvent deployEvent) {
        _adminEventProcessor.moduleRedeployed(deployEvent);	
    } 
    
    public void moduleEnabled(ModuleDeployEvent deployEvent) {
    } 
    
    public void moduleDisabled(ModuleDeployEvent deployEvent) {
    }         
    //end HERCULES:add    


    // -------------------------------------- Monitoring Level Event Processing

    public void registerMonitoringLevelEvents() {
        MonitoringRegistry monitoringRegistry =
                _serverContext.getMonitoringRegistry();
        monitoringRegistry.registerMonitoringLevelListener(
                                        this, MonitoredObjectType.SERVLET);
    }

    public void unregisterMonitoringLevelEvents() {
        MonitoringRegistry monitoringRegistry =
                _serverContext.getMonitoringRegistry();
        monitoringRegistry.unregisterMonitoringLevelListener(this);
    }

    public void setLevel(MonitoringLevel level) {
        // deprecated, ignore
    }

    public void changeLevel(MonitoringLevel from, MonitoringLevel to, 
			    Stats handback) {
        // deprecated, ignore
    }

    public void changeLevel(MonitoringLevel from, MonitoringLevel to,
                            MonitoredObjectType type) {
        if (MonitoredObjectType.SERVLET.equals(type)) {
            monitoringLevel = to;
            if(MonitoringLevel.OFF.equals(to)) {
                this.resetMonitorStatistics();
            }
        }
    }

    /**
     * Returns the current monitoring level.
     *
     * @return The current monitoring level
     */
    public static MonitoringLevel getMonitoringLevel() {
        return monitoringLevel;
    }
    
    /**
     * Returns true if monitoring is enabled, false otherwise.
     *
     * @return true if monitoring is enabled, false otherwise
     */
    public static boolean isMonitoringEnabled() {
        return (!MonitoringLevel.OFF.equals(monitoringLevel));
    }
    
    private void resetMonitorStatistics() {
        MonitorUtil.resetMonitorStats(_embedded, _serverContext);
    }


    // -------------------------------------------------------- Public Methods

    /**
     * Create a virtual server/host.
     */
    public VirtualServer createVS(String vsID,
                                  com.sun.enterprise.config.serverbeans.VirtualServer vsBean,
                                  String docroot,
                                  String logFile,
                                  MimeMap mimeMap,
                                  HttpProtocol httpProtocol) {

        // Initialize the docroot
        VirtualServer vs = (VirtualServer) _embedded.createHost(vsID,
                                                                vsBean,
								docroot,
								logFile,
                                                                mimeMap);
                    
        configureVirtualServerState(vs, vsBean);
        configureRemoteAddressFilterValve(vs, vsBean);
        configureRemoteHostFilterValve(vs, vsBean, httpProtocol);
        configureSSOValve(vs, vsBean);
        configureRedirect(vs, vsBean);
        configureErrorPage(vs, vsBean);

        return vs;
    }

    // ------------------------------------------------------ Lifecycle Methods


    /**
     * Add a lifecycle event listener to this component.
     *
     * @param listener The listener to add
     */
    public void addLifecycleListener(LifecycleListener listener) {
        _lifecycle.addLifecycleListener(listener);
    }


    /**
     * Remove a lifecycle event listener from this component.
     *
     * @param listener The listener to remove
     */
    public void removeLifecycleListener(LifecycleListener listener) {
        _lifecycle.removeLifecycleListener(listener);
    }
    
    private void doSchemaCheck() { 
        SchemaUpdater schemaUpdater =
            _serverContext.getPluggableFeatureFactory().getSchemaUpdater();
        try {
            schemaUpdater.doSchemaCheck();
        } catch (Exception ex) {
            _logger.log(Level.SEVERE, "schemaupdater.error", ex);
        }
    }    

    /**
     * Prepare for the beginning of active use of the public methods of this
     * component.  This method should be called before any of the public
     * methods of the component are utilized.
     *
     * @exception IllegalStateException if this component has already been
     *  started
     * @exception LifecycleException if this component detects a fatal error
     *  that prevents this component from being used
     */
    public void start() throws LifecycleException {
        if (_started) {
            String msg = _rb.getString("webcontainer.alreadyStarted");
            throw new LifecycleException(msg);
        }

        //HERCULES:mod
        registerAdminEvents();
        registerMonitoringLevelEvents();
        initHealthChecker();
        long btime = 0L;
        if (_logger.isLoggable(Level.FINE)) {
            _logger.fine("before schema check");
            btime = System.currentTimeMillis();
        }
        doSchemaCheck();
        if (_logger.isLoggable(Level.FINE)) {
            _logger.fine("after schema check time: " + (System.currentTimeMillis() - btime));
        }        
        //end HERCULES:mod

        ejbWebServiceRegistryListener.register();

        Engine[] engines =  _embedded.getEngines();

        for (int j=0; j<engines.length; j++) {
            Container[] vsList = engines[j].findChildren();
            for (int i = 0; i < vsList.length; i++) {
                // Load all the standalone web modules for each VS
              loadWebModules((VirtualServer)vsList[i]);
            }
        }

        // Load the web modules specified in each j2ee-application
        loadAllJ2EEApplicationWebModules(true);

        //
        // Create and load a default context for those virtual servers
        // who do not have one configured already (i.e. when the configuration
        // for the virtual-server element does not specify a default-web-module
        // and none of the web modules (on a virtual server) are loaded at
        // a context-root=""
        //
        loadSystemDefaultWebModules();

        _lifecycle.fireLifecycleEvent(START_EVENT, null);
        _started = true;
         // start the embedded container
        _embedded.start();

        if (_reloadingEnabled) {
            // Enable dynamic reloading (via the .reload file) for all
            // standalone web-modules that are marked as enabled
 
            Applications appsBean = null;
            try {
            	appsBean = ServerBeansFactory.getApplicationsBean(_configContext);
            } catch (ConfigException e) {
               String msg = _rb.getString("webcontainer.appsConfigError");
               _logger.log(Level.SEVERE, msg, e);
            }

            _reloadManager = new StandaloneWebModulesManager(_id,
                                                             _modulesRoot,
                                                             _pollInterval);
            if (appsBean != null) {
                com.sun.enterprise.config.serverbeans.WebModule[] wmBeans = appsBean.getWebModule();
                if (wmBeans != null && wmBeans.length > 0) {
                    _reloadManager.addWebModules(wmBeans);
                }
            }
        }
        enableAllWSEndpoints();
    }

    /**
     * Gracefully terminate the active use of the public methods of this
     * component.  This method should be the last one called on a given
     * instance of this component.
     *
     * @exception IllegalStateException if this component has not been started
     * @exception LifecycleException if this component detects a fatal error
     *  that needs to be reported
     */
    public void stop() throws LifecycleException {
        // Validate and update our current component state
        if (!_started) {
            String msg = _rb.getString("webcontainer.notStarted");
            throw new LifecycleException(msg);
        }
          
        ejbWebServiceRegistryListener.unregister();

        //HERCULES:mod
        unregisterAdminEvents();
	unregisterMonitoringLevelEvents();
        stopHealthChecker();
        
        WebContainerStartStopOperation startStopOperation = 
            this.getWebContainerStartStopOperation();
        /* TODO: remove after testing
        //added the pluggable interface way of getting the start/stop operation
        WebContainerFeatureFactory webContainerFeatureFactory =
            _serverContext.getPluggableFeatureFactory().getWebContainerFeatureFactory();
        WebContainerStartStopOperation startStopOperation =
            webContainerFeatureFactory.getWebContainerStartStopOperation();
        startStopOperation.init(_embedded);
         */        
        ArrayList shutdownCleanupCapablesList = startStopOperation.doPreStop();
        //end HERCULES:mod
         
        _started = false;

        // stop the embedded container         
        try{
            _embedded.stop();
        } catch (LifecycleException ex){
            if (ex.getMessage().indexOf("has not been started") == -1){
                throw ex;
            }
        }

        if (_reloadManager != null) {
            // Remove the entries from the reload monitor thread corresponding
            // to this web container object that is being stopped
            _reloadManager.stop();
            _reloadManager = null;
        }
         
        //HERCULES:mod 
        startStopOperation.doPostStop(shutdownCleanupCapablesList);
        //end HERCULES:mod         
     }
    
    /**
     * Get the webContainerStartStopOperation
     * used for doing shutdown cleanup work 
     */    
    public WebContainerStartStopOperation getWebContainerStartStopOperation() {
        
        //added the pluggable interface way of getting the start/stop operation
        WebContainerFeatureFactory webContainerFeatureFactory =
            _serverContext.getPluggableFeatureFactory().getWebContainerFeatureFactory();
        WebContainerStartStopOperation startStopOperation =
            webContainerFeatureFactory.getWebContainerStartStopOperation();
        
        startStopOperation.init(_embedded);
        return startStopOperation;
    }    

    // -------------------------------------------------------- Private Methods

    /**
     * Loads all the standalone web-modules that are hosted (as specified by
     * the server configuration) under the specified virtual server.
     */
    protected void loadWebModules(VirtualServer vs) {
        WebModuleConfig wmInfo = null;
        
        loadDefaultWebModule(vs,_serverBean);
        
        // Get a list of the web modules to be loaded on this virtual server
        List modules = vs.getWebModules(_serverBean, _modulesRoot);
        if ((modules != null) && (modules.size() > 0)) {
            ListIterator iterator = modules.listIterator();
            while (iterator.hasNext()) {
                wmInfo = (WebModuleConfig) iterator.next();
                com.sun.enterprise.config.serverbeans.WebModule webBean =
                     wmInfo.getBean();
                 // load the stand alone web module if it is enabled
                 if (webBean != null && 
                     isEnabled(webBean.getConfigContext(), webBean.getName())) {
                     loadStandaloneWebModule(vs, wmInfo);
                 }
            }
        }
    }
    
    
    /**
     * Add the default web-module to a virtual-server.
     * @param vs the virtual-server which doesn't have a default web-module.
     * @param serverBean the server config bean used to configure the 
     *                   default web module.
     */    
    public void loadDefaultWebModule(VirtualServer vs, 
                                                Server serverBean){        
        WebModuleConfig wmInfo = vs.getUserDefaultWebModuleConfig(serverBean);
        if (wmInfo != null) {
            // If there is a 'default-web-module' for the virtual server,
            // load it first, thus preventing other web modules that may
            // have a context-root = "" from getting loaded as the default
            // web module for this virtual server
            loadStandaloneWebModule(vs, wmInfo);
        }
    }
    
    
    /**
     * Creates and configures a default context for each virtual server
     * that doesn't have one configured already.
     */
    protected void loadSystemDefaultWebModules() {

        Engine[] engines =  _embedded.getEngines();

        for (int j=0; j<engines.length; j++) {
            Container[] vsArray = engines[j].findChildren();
            for (int i = 0; i < vsArray.length; i++) {
                if (vsArray[i] instanceof VirtualServer) {
                    VirtualServer vs = (VirtualServer) vsArray[i];
                    WebModuleConfig wmInfo = 
                        vs.createSystemDefaultWebModuleIfNecessary();
                    if (wmInfo != null) {
                        loadStandaloneWebModule(vs, wmInfo);
                    }
                }
            }
        }
    }

    /**
     * Load the specified web module as a standalone module on the specified
     * virtual server.
     */
    protected void loadStandaloneWebModule(VirtualServer vs,
                                         WebModuleConfig wmInfo) {
        loadWebModule(vs, wmInfo, "null");
    }

    /**
     * Loads all the web modules that are configured for every
     * j2ee-application specified in server.xml.
     */
    protected void loadAllJ2EEApplicationWebModules() {
        loadAllJ2EEApplicationWebModules(false);
    }

    /**
     * Loads all the web modules that are configured for every
     * j2ee-application specified in server.xml.
     *
     * @param isStartUp true, if this is called during server startup
     */
    private void loadAllJ2EEApplicationWebModules(boolean isStartUp) {

        Applications appsBean = null;
        try {
            appsBean = ServerBeansFactory.getApplicationsBean(_configContext);
        } catch (ConfigException e) {
            String msg = _rb.getString("webcontainer.appsConfigError");
            _logger.log(Level.SEVERE, msg, e);
        }

        if (appsBean != null) {
            J2eeApplication[] j2eeAppBeans = appsBean.getJ2eeApplication();
            if (j2eeAppBeans != null) {
                for (int i = 0; i < j2eeAppBeans.length; i++) {
                    //Begin EE: 4927099 - load only associated applications
                    if ( isReferenced(j2eeAppBeans[i].getName()) ) {
                        loadJ2EEApplicationWebModules(j2eeAppBeans[i]);
                    }
                    //End EE: 4927099 - load only associated applications
                }
            }
        }
    }

    /**
     * Loads all the web modules that are configured for the specified
     * j2ee-application.
     */
    public void loadJ2EEApplicationWebModules(J2eeApplication j2eeAppBean) {
        if ((j2eeAppBean != null) && 
            isEnabled(j2eeAppBean.getConfigContext(), j2eeAppBean.getName())) {
            String id = j2eeAppBean.getName();
            String location = j2eeAppBean.getLocation();
            String resourceType = j2eeAppBean.getObjectType();

            ApplicationRegistry registry = ApplicationRegistry.getInstance();
            ClassLoader appLoader = registry.getClassLoaderForApplication(id);
            if (appLoader != null) {
                Application appDesc = registry.getApplication(appLoader); 
             
                // Check to see if this app had deployed successfully (4663247)
                if(appDesc == null){ 
                    Object[] params = { id };
                    _logger.log(Level.SEVERE, "webcontainer.notLoaded",
                                params);
                } else {
                    //Hercules: add 
                    ConfigContext eventConfigContext = j2eeAppBean.getConfigContext();
                    //end Hercules: add                     
                    String j2eeApplication = appDesc.getRegistrationName();
                    Set wbds = appDesc.getWebBundleDescriptors();
                    WebBundleDescriptor wbd = null;
                    com.sun.enterprise.config.serverbeans.WebModule wm = null;
                    WebModuleConfig wmInfo = null;
                    for (Iterator itr = wbds.iterator(); itr.hasNext(); ) {
                        StringBuffer dir = new StringBuffer(location);
                        wbd = (WebBundleDescriptor) itr.next();
                        String moduleName = wbd.getModuleDescriptor().getArchiveUri();
                        dir.append(File.separator);
                        dir.append(FileUtils.makeFriendlyFilename(moduleName));

                        wm = new com.sun.enterprise.config.serverbeans.WebModule();
                        //Hercules add
                        wm.setConfigContext(eventConfigContext);
                        //end Hercules add                        
                        wm.setName(moduleName);
                        wm.setContextRoot(wbd.getContextRoot());
                        wm.setLocation(dir.toString());
                        wm.setEnabled(true);
                        wm.setObjectType(resourceType);

                        wmInfo = new WebModuleConfig();
                        wmInfo.setBean(wm);
                        wmInfo.setDescriptor(wbd);
                        wmInfo.setParentLoader(appLoader);
                        wmInfo.setVirtualServers(
                                getVirtualServers(id,
                                        j2eeAppBean.getConfigContext()));
                        loadWebModule(wmInfo, j2eeApplication);
                    }
                }//end if(appDesc != null)
            }
        }
    }

   /**
     * Whether or not a component (either an application or a module) should be
     * enabled is defined by the "enable" attribute on both the
     * application/module element and the application-ref element.
     *
     * @param config The dynamic ConfigContext
     * @param moduleName The name of the component (application or module)
     * @return boolean
     */
    protected boolean isEnabled (ConfigContext config, String moduleName) {
        try {
            if (config == null) {
                config = _configContext;
            }
 
            ConfigBean app = ApplicationHelper.findApplication(config, 
                moduleName);
            Server server = ServerBeansFactory.getServerBean(config);
            ApplicationRef appRef = server.getApplicationRefByRef(moduleName);

            return ((app != null && app.isEnabled()) &&
                        (appRef != null && appRef.isEnabled()));
        } catch (ConfigException e) {
            _logger.log(Level.WARNING, "Error finding " + moduleName, e);

            //If there is anything wrong, do not enable the module
            return false;
        }
    }

    /**
     * Creates and configures a web module for each virtual server
     * that the web module is hosted under.
     *
     * If no virtual servers are specified, then the web module is
     * loaded on EVERY virtual server.
     */
    public List<Throwable> loadWebModule(
        WebModuleConfig wmInfo, String j2eeApplication) {

        String vsIDs = wmInfo.getVirtualServers();
        List vsList = StringUtils.parseStringList(vsIDs, " ,");
        boolean loadToAll = (vsList == null) || (vsList.size() == 0);
        boolean loadAtLeastToOne = false;

        Engine[] engines =  _embedded.getEngines();

        List<Throwable> throwables = new ArrayList();
        for (int j=0; j<engines.length; j++) {
            Container[] vsArray = engines[j].findChildren();
            for (int i = 0; i < vsArray.length; i++) {
                if (vsArray[i] instanceof VirtualServer) {
                    VirtualServer vs = (VirtualServer) vsArray[i];

                    /*
                     * Fix for bug# 4913636:
                     * If the vsList is null and the virtual server is
                     * __asadmin, continue with next iteration
                     * because we don't want to load user apps on __asadmin
                     */
                    if (vs.getID().equals(ADMIN_VS) && loadToAll) {
                        continue;
                    }

                    if ( loadToAll 
                            || vsList.contains(vs.getID()) 
                            || verifyAlias(vsList,vs)){

                        Throwable t = loadWebModule(vs, wmInfo, j2eeApplication);
                        if (t != null) {
                            throwables.add(t);
                        }
                        loadAtLeastToOne = true;
                    }
                }
            }
        }
        if (!loadAtLeastToOne) {
            Object[] params = {wmInfo.getName(), vsIDs};
            _logger.log(Level.SEVERE, "webcontainer.moduleNotLoadedToVS",
                            params);
        }
        return throwables;
    }


    /**
     * Deploy on aliases as well as host.
     */
    private boolean verifyAlias(List vsList,VirtualServer vs){
        for(int i=0; i < vs.getAliases().length; i++){
            if (vsList.contains(vs.getAliases()[i]) ){
                return true;
            }
        }
        return false;
    }
    
    /**
     * Creates and configures a web module and adds it to the specified
     * virtual server.
     */
    protected Throwable loadWebModule(VirtualServer vs, WebModuleConfig wmInfo,
                                 String j2eeApplication) {
        String wmName = wmInfo.getName();
        if (wmName.indexOf(Constants.NAME_SEPARATOR) != -1) {
            wmInfo.setWorkDirBase(_appsWorkRoot);
            // START S1AS 6178005
            wmInfo.setStubBaseDir(appsStubRoot);
            // END S1AS 6178005
        } else {
            wmInfo.setWorkDirBase(_modulesWorkRoot);
            // START S1AS 6178005
            wmInfo.setStubBaseDir(modulesStubRoot);
            // END S1AS 6178005
        }

        String wmContextPath = wmInfo.getContextPath();
        String displayContextPath = null;
        if (wmContextPath.equals(""))
            displayContextPath = "/";
        else
            displayContextPath = wmContextPath;

        HashMap adHocPaths = null;
        HashMap adHocSubtrees = null;
        WebModule ctx = (WebModule)vs.findChild(wmContextPath);
        if (ctx != null) {
            if (ctx instanceof AdHocWebModule) {
                /*
                 * Found ad-hoc web module which has been created by web
                 * container in order to store mappings for ad-hoc paths
                 * and subtrees.
                 * All these mappings must be propagated to the context
                 * that is being deployed.
                 */
                if (ctx.hasAdHocPaths()) {
                    adHocPaths = ctx.getAdHocPaths();
                }
                if (ctx.hasAdHocSubtrees()) {
                    adHocSubtrees = ctx.getAdHocSubtrees();
                }
                vs.removeChild(ctx);
            } else if (Constants.DEFAULT_WEB_MODULE_NAME
                    .equals(ctx.getModuleName())) {
                /*
                 * Dummy context that was created just off of a docroot,
                 * (see
                 * VirtualServer.createSystemDefaultWebModuleIfNecessary()).
                 * Unload it so it can be replaced with the web module to be
                 * loaded
                 */
                unloadWebModule(wmContextPath,
                                ctx.getJ2EEApplication(),
                                vs.getName(),
                                null);
            } else if (!ctx.getAvailable()){
                /*
                 * Context has been marked unavailable by a previous
                 * call to disableWebModule. Mark the context as available and 
                 * return
                 */
                ctx.setAvailable(true);
                return null;
            } else {
                Object[] params = { vs.getID(), displayContextPath, wmName };
                _logger.log(Level.WARNING, "webcontainer.duplicateContextRoot",
                            params);
                return null;
            }
        }

        Object[] params = { wmName, vs.getID(), displayContextPath };
        if (wmName.equals(Constants.DEFAULT_WEB_MODULE_NAME))
            _logger.log(Level.FINE, "webcontainer.loadModule", params);
        else
            _logger.log(Level.INFO, "webcontainer.loadModule", params);

        String docBase = null;
        if (JWS_APPCLIENT_MODULE_NAME.equals(wmName)) {
            File installRootFile = new File(System.getProperty("com.sun.aas.installRoot"));
            String path = installRootFile.toURI().getPath();
            if (OS.isWindows()) {
                path = path.substring(1); // On Windows, skip the slash before the device
            }
            docBase = path;
        } else {
            docBase = wmInfo.getLocation();
        }
        ctx = (WebModule) _embedded.createContext(wmContextPath,
                                                  docBase,
                                                  vs.getDefaultWebXmlLocation(),
                                                  useDOLforDeployment,
                                                  wmInfo.getDescriptor());

        // Set JSR 77 object name and attributes
        String engineName = vs.getParent().getName();
        String j2eeServer = _serverContext.getInstanceName();
        String domain = _serverContext.getDefaultDomainName();
        String server = domain + ":j2eeType=J2EEServer,name=" + j2eeServer;
        String[] javaVMs = J2EEModuleUtil.getjavaVMs();
        ctx.setDomain(domain);

        ctx.setJ2EEServer(j2eeServer);
        ctx.setJ2EEApplication(j2eeApplication);   
        ctx.setEngineName(engineName);
        ctx.setServer(server);
        ctx.setJavaVMs(javaVMs);
        ctx.setCachingAllowed(true);
        ctx.setCacheControls(vs.getCacheControls());
 
        if (adHocPaths != null) {
            ctx.addAdHocPaths(adHocPaths);
        }
        if (adHocSubtrees != null) {
            ctx.addAdHocSubtrees(adHocSubtrees);
        }

        // Object containing web.xml information
        WebBundleDescriptor wbd = wmInfo.getDescriptor();

        //Set the context root
        if (wmInfo.getBean() != null) {
            String contextRoot = wmInfo.getBean().getContextRoot();
            ctx.setContextRoot(contextRoot);
            if (wbd != null) {
                wbd.setContextRoot(contextRoot);
            }
        } else {
            // Should never happen.
            _logger.log(Level.WARNING, "Unable to set context root", wmInfo);
        }        

        //
        // Ensure that the generated directory for JSPs in the document root 
        // (i.e. those that are serviced by a system default-web-module)
        // is different for each virtual server.
        //
        String wmInfoWorkDir = wmInfo.getWorkDir();
        if (wmInfoWorkDir != null) {
            StringBuffer workDir = new StringBuffer(wmInfo.getWorkDir());
            if (wmName.equals(Constants.DEFAULT_WEB_MODULE_NAME)) {
                workDir.append("-");
                workDir.append(FileUtils.makeFriendlyFilename(vs.getID()));
            }
            ctx.setWorkDir(workDir.toString());
        }

        ClassLoader parentLoader = wmInfo.getParentLoader();
        if (parentLoader == null) {
            // Use the shared classloader as the parent for all
            // standalone web-modules
            parentLoader = _serverContext.getSharedClassLoader();
        }
        ctx.setParentClassLoader(parentLoader);

        Throwable exception = null; 
        Switch sw = Switch.getSwitch();
        try{
            // Determine if an alternate DD is set for this web-module in 
            // the application
            if (wbd != null) {
                String altDDName = wbd.getModuleDescriptor().
                                        getAlternateDescriptor();
                if (altDDName != null) {
                    // we should load the alt dd from generated/xml directory
                    // first, then fall back to original app location.

                    // if we have alt dd, it must be an embedded web module
                    String appName =  wmName.substring(0, 
                        wmName.indexOf(Constants.NAME_SEPARATOR));
                    String appLoc = 
                        appsManager.getGeneratedXMLLocation(appName);
                    if (! FileUtils.safeIsDirectory(appLoc)) {
                        appLoc = wmInfo.getLocation()+"/..";
                    }

                    if (altDDName.startsWith("/")) {
                        altDDName = appLoc+altDDName.trim();
                    }
                    else {
                        altDDName = appLoc+"/"+altDDName.trim();
                    }
                    Object[] objs = {altDDName, wmName};
                    _logger.log(Level.INFO, "webcontainer.altDDName", objs);
                    ctx.setAltDDName(altDDName);

                }
                // time to update the Web Services related information in 
                // our runtime jsr77 mbeans. We publish two extra properties
                // hasWebServices and endpointAddresses for webservices
                // enable web applications.
                if (wbd.hasWebServices()) {
                    ctx.setHasWebServices(true);
                    
                    // creates the list of endpoint addresses
                    String[] endpointAddresses;
                    WebServicesDescriptor webService = wbd.getWebServices();
                    Vector endpointList = new Vector();
                    for (Iterator endpoints = webService.getEndpoints().iterator();
                    endpoints.hasNext();) {
                        WebServiceEndpoint wse = (WebServiceEndpoint) endpoints.next();
                        if (wbd.getContextRoot()!=null) {
                            endpointList.add(wbd.getContextRoot() + "/"
                                             + wse.getEndpointAddressUri());
                        } else {
                            endpointList.add(wse.getEndpointAddressUri());
                        }
                    }
                    endpointAddresses = new String[endpointList.size()];
                    endpointList.copyInto(endpointAddresses);
                    
                    ctx.setEndpointAddresses(endpointAddresses);
                    
                } else {
                    ctx.setHasWebServices(false);
                }
            }

            // Object containing sun-web.xml information
            SunWebApp iasBean = null;

            // The default context is the only case when wbd == null
            if (wbd != null) 
                iasBean = wbd.getSunDescriptor();

            // set the sun-web config bean
            ctx.setIasWebAppConfigBean(iasBean);
            ctx.setID(wmName);

            // Configure the class loader delegation model, classpath etc
            Loader loader = configureLoader(ctx, iasBean, wmInfo);
            
            // Set the class loader on the DOL object 
            if (wbd != null && wbd.hasWebServices())
                wbd.addExtraAttribute("WEBLOADER", loader);

            // Enable dynamic reloading
            if (_reloadingEnabled) {
                if (_reloadManager == null) {
                    _reloadManager = new StandaloneWebModulesManager(
                                                            _id,
                                                            _modulesRoot,
                                                            _pollInterval);
                }
                _reloadManager.addWebModule(wmInfo.getBean());
            }

            // Configure SingleThreadedServletPools, work/tmp directory etc
            configureMiscSettings(ctx, iasBean, vs, displayContextPath);

            // Configure the session manager and other related settings
            // HERCULES:mod - take into account if app is distributable
            // passing in WebBundleDescriptor which has info about whether
            // app is distributable
            configureSessionSettings(ctx, iasBean, wbd, wmInfo);
            // END HERCULES:mod

            // set i18n info from locale-charset-info tag in sun-web.xml
            ctx.setI18nInfo();

            if (wbd != null) {
                String resourceType = wmInfo.getBean().getObjectType();
                boolean isSystem = (resourceType != null &&
                    resourceType.startsWith("system-"));
                ctx.setRealm(new RealmAdapter(wbd, isSystem));
                configureSecurity(wbd, isSystem);
                sw.setDescriptorFor(ctx, wbd);

                // post processing DOL object for standalone web module
                if (wbd.getApplication() != null &&
                    wbd.getApplication().isVirtual()) {
                    wbd.visit((WebBundleVisitor) new WebValidatorWithoutCL());
                }

                sw.getNamingManager().bindObjects(wbd);
            }

            // Add virtual server mime mappings, if present
            addMimeMappings(ctx, vs.getMimeMap());

        } catch (Throwable ex){
            exception = ex;
        }

        if (wbd != null && wbd.getApplication() != null) {
            // no dummy web module
            sw.setDescriptorFor(ctx, wbd);
            String moduleName;
            // S1AS BEGIN WORKAROUND FOR 6174360
            if (wbd.getApplication().isVirtual()) {
                // this is a standalone module
                moduleName = wbd.getApplication().getRegistrationName();
            } else {
                moduleName = wbd.getModuleDescriptor().getArchiveUri();
            }
            // S1AS END WORKAROUND FOR 6174360
            ctx.setModuleName(moduleName);
        } else {
            ctx.setModuleName(Constants.DEFAULT_WEB_MODULE_NAME);
        }

        try {
            vs.addChild(ctx);
        } catch (Throwable ex){
            exception = ex;
        }
        
        if (exception != null){
            ctx.setAvailable(false);

            Object[] objs = {wmName, exception};
            _logger.log(Level.SEVERE, "webcontainer.webModuleDisabled", objs);
            
        }
        enableWSMonitoring(wbd, j2eeServer);
        return exception;
    }


    /**
    * Generate the JSR 115 policy file for a web application, bundled
    * within a ear or deployed as a standalone war file.
    *
    * Implementation note: If the generated file doesn't contains
    * all the permission, the role mapper is probably broken.
    */
    protected void configureSecurity(WebBundleDescriptor wbd,
                                     boolean isSystem) {
        try{
            webSecurityManagerFactory.newWebSecurityManager(wbd);
	    String context = WebSecurityManager.getContextID(wbd);
	    SecurityUtil.generatePolicyFile(context);
        }catch(Exception ce){
            _logger.log(Level.SEVERE, "webcontainer.configureSecurity", ce);
            throw new RuntimeException(ce);
        }
    }
    
    /**
     * Utility Method to access the ServerContext
     */
     public ServerContext getServerContext() {
        return _serverContext;
    }
    
    /**
     * Configure the class loader for the web module based on the 
     * settings in sun-web.xml's class-loader element (if any).
     */
    private Loader configureLoader(WebModule ctx, SunWebApp bean,
                                   WebModuleConfig wmInfo) {

        com.sun.enterprise.deployment.runtime.web.ClassLoader clBean = null;

        Loader loader = _embedded.createLoader(ctx.getParentClassLoader());

        if (bean != null) {            
            clBean = bean.getClassLoader();
        }
        if (clBean != null) {
            configureLoaderAttributes(loader, clBean, ctx);
            configureLoaderProperties(loader, clBean);
        } else {
            loader.setDelegate(true);
        }

        // START S1AS 6178005
        String stubPath = wmInfo.getStubPath();
        if (stubPath != null) {
            loader.addRepository("file:" + stubPath + File.separator);
        }
        // END S1AS 6178005

        addLibs(loader, ctx);

        // START PE 4985680
        /**
         * Adds the given package name to the list of packages that may 
         * always be overriden, regardless of whether they belong to a 
         * protected namespace
         */
        String packagesName = 
            System.getProperty("com.sun.enterprise.overrideablejavaxpackages");

        if (packagesName != null) {
            List overridablePackages = 
                    StringUtils.parseStringList(packagesName, " ,");        
            for( int i=0; i < overridablePackages.size(); i++){ 
                loader.addOverridablePackage((String)overridablePackages.get(i));
            }
        }
        // END PE 4985680

        ctx.setLoader(loader);
        return loader;
    }

    /**
     * Configure miscellaneous settings such as the pool size for
     * single threaded servlets, specifying a temporary directory other
     * than the default etc.
     *
     * Since the work directory is used when configuring the session manager
     * persistence settings, this method must be invoked prior to
     * <code>configureSessionSettings</code>.
     */
    private void configureMiscSettings(WebModule ctx, SunWebApp bean,
                                       VirtualServer vs, String contextPath) {

        /*
         * Web app inherits setting of allowLinking property from vs on which
         * it is being deployed, but may override it using allowLinking
         * property in its sun-web.xml
         */
        boolean allowLinking = vs.getAllowLinking();

        if ((bean != null) && (bean.sizeWebProperty() > 0)) {
            WebProperty[] props = bean.getWebProperty();
            for (int i = 0; i < props.length; i++) {

                String name = props[i].getAttributeValue("name");
                String value = props[i].getAttributeValue("value");
                if (name == null || value == null) {
                    throw new IllegalArgumentException(
                        _rb.getString("webcontainer.nullWebProperty"));
                }

                if (name.equalsIgnoreCase("singleThreadedServletPoolSize")) {
                    int poolSize = ctx.getSTMPoolSize();
                    try {
                        poolSize = Integer.parseInt(value);
                    } catch (NumberFormatException e) {
                        Object[] params =
                            { value, contextPath, Integer.toString(poolSize) };
                        _logger.log(Level.WARNING,
                                    "webcontainer.invalidServletPoolSize",
                                    params);
                    }
                    if (poolSize > 0) {
                        ctx.setSTMPoolSize(poolSize);
                    }

                } else if (name.equalsIgnoreCase("tempdir")) {
                    ctx.setWorkDir(value);
                } else if (name.equalsIgnoreCase("crossContextAllowed")) {
                    boolean crossContext = ConfigBean.toBoolean(value);
                    ctx.setCrossContext(crossContext);
		} else if (name.equalsIgnoreCase("allowLinking")) {
                    allowLinking = ConfigBean.toBoolean(value);
                // START S1AS8PE 4817642
                } else if (name.equalsIgnoreCase("reuseSessionID")) {
                    boolean reuse = ConfigBean.toBoolean(value);
                    ctx.setReuseSessionID(reuse);
                    if (reuse) {
                        Object[] params = { contextPath,
                                            vs.getID() };
                        _logger.log(Level.WARNING,
                                    "webcontainer.sessionIDsReused",
                                    params);
                    }
                // END S1AS8PE 4817642
                } else if(name.equalsIgnoreCase("useResponseCTForHeaders")) {
                    if(value.equalsIgnoreCase("true")) {
                        ctx.setResponseCTForHeaders();   
                    }
                } else if(name.equalsIgnoreCase("encodeCookies")) {
                    boolean flag = ConfigBean.toBoolean(value);
                    ctx.setEncodeCookies(flag);
                // START RIMOD 4642650 
                } else if (name.equalsIgnoreCase("relativeRedirectAllowed")) {
                    boolean relativeRedirect = ConfigBean.toBoolean(value);
                    ctx.setAllowRelativeRedirect(relativeRedirect);
                // END RIMOD 4642650 
                } else if (name.equalsIgnoreCase("fileEncoding")) {
                    ctx.setFileEncoding(value);
                } else if (name.equalsIgnoreCase("enableTldValidation")
                        &&  ConfigBean.toBoolean(value)) {
                    ctx.setTldValidation(true);
                } else if (name.equalsIgnoreCase("enableTldNamespaceAware")
                        &&  ConfigBean.toBoolean(value)) {
                    ctx.setTldNamespaceAware(true);
                } else {
                    Object[] params = { name, value };
                    _logger.log(Level.WARNING, "webcontainer.invalidProperty",
                                params);
                }
            }
        }

        ctx.setAllowLinking(allowLinking);
    }
    
    /**
     * Create and configure the session manager for this web application
     * according to the persistence type specified.
     *
     * Also configure the other aspects of session management for this
     * web application according to the values specified in the session-config
     * element of sun-web.xml (and whether app is distributable)
     * HERCULES:mod method signature added wbd
     */
    private void configureSessionSettings(WebModule ctx,
                                          SunWebApp bean,
                                          WebBundleDescriptor wbd,
                                          WebModuleConfig wmInfo) {

        SessionConfig cfg = null;
        SessionManager smBean = null;
        SessionProperties sessionBean = null;
        CookieProperties cookieBean = null;

        if (bean != null)
            cfg = bean.getSessionConfig();

        if (cfg != null)
            smBean = cfg.getSessionManager();

        if (cfg != null)
            sessionBean = cfg.getSessionProperties();

        if (cfg != null)
            cookieBean = cfg.getCookieProperties();
        
        //Hercules: mod  - passing wbd and wmInfo into these methods
        configureSessionManager(ctx, smBean, wbd, wmInfo);
        //configureSessionManager(ctx, smBean, wbd);
        configureSession(ctx, sessionBean, wbd);
        //end HERCULES:mod
        configureCookieProperties(ctx, cookieBean);        
    } 
    
    /**
     * The application id for this web module
     * HERCULES:add
     */    
    public String getApplicationId(WebModule wm) {
        return wm.getID();
    } 


    /**
     * Return the Absolute path for location where all the deployed
     * standalone modules are stored for this Server Instance.
     */
    public String getModulesRoot() {
        return _modulesRoot;
    }

    
    /**
     * Configure the session manager according to the persistence-type
     * specified in the <session-manager> element and the related 
     * settings in the <manager-properties> and <store-properties> elements
     * in sun-web.xml.
     * HERCULES:modified
     */
    private void configureSessionManager(WebModule ctx,
                                         SessionManager smBean,
                                         WebBundleDescriptor wbd,
                                         WebModuleConfig wmInfo) {
        
        PersistenceType persistence = PersistenceType.MEMORY;
        String frequency = null;
        String scope = null;
        
        SessionManagerConfigurationHelper configHelper = 
            new SessionManagerConfigurationHelper(ctx, smBean, wbd, wmInfo);
        
        persistence = configHelper.getPersistenceType();
        frequency = configHelper.getPersistenceFrequency();
        scope = configHelper.getPersistenceScope();

        if (_logger.isLoggable(Level.FINEST)) {        
            _logger.finest("IN WebContainer>>ConfigureSessionManager before builder factory");
            _logger.finest("FINAL_PERSISTENCE-TYPE IS = "
                           + persistence.getType());
            _logger.finest("FINAL_PERSISTENCE_FREQUENCY IS = " + frequency);
            _logger.finest("FINAL_PERSISTENCE_SCOPE IS = " + scope);          
        }

	PersistenceStrategyBuilderFactory factory = 
            new PersistenceStrategyBuilderFactory();
        PersistenceStrategyBuilder builder = 
            factory.createPersistenceStrategyBuilder(persistence.getType(),
                                                     frequency, scope, ctx);
        if (_logger.isLoggable(Level.FINEST)) {
            _logger.finest("PersistenceStrategyBuilder class = "
                           + builder.getClass().getName());
        }

        builder.setLogger(_logger);
        builder.initializePersistenceStrategy(ctx, smBean);
    }
    

    /**
     * Get the persistence frequency for this web module
     * (this is the value from sun-web.xml if defined
     * @param the session manager config bean
     * HERCULES:add
     */
    private String getPersistenceFrequency(SessionManager smBean) {
        String persistenceFrequency = null;        
        ManagerProperties mgrBean = smBean.getManagerProperties();
        if ((mgrBean != null) && (mgrBean.sizeWebProperty() > 0)) {
            WebProperty[] props = mgrBean.getWebProperty();
            for (int i = 0; i < props.length; i++) {

                String name = props[i].getAttributeValue(WebProperty.NAME);
                String value = props[i].getAttributeValue(WebProperty.VALUE);
                if (name == null || value == null) {
                    throw new IllegalArgumentException(
                        _rb.getString("webcontainer.nullWebProperty"));
                }

                if (name.equalsIgnoreCase("persistenceFrequency")) {
                    persistenceFrequency = value;
                    break;
                }
            }
        }
        return persistenceFrequency;
    } 
    
    /**
     * Get the persistence scope for this web module
     * (this is the value from sun-web.xml if defined
     * @param the session manager config bean
     * HERCULES:add
     */    
    private String getPersistenceScope(SessionManager smBean) {
        String persistenceScope = null;
        StoreProperties storeBean = smBean.getStoreProperties();
        if ((storeBean != null) && (storeBean.sizeWebProperty() > 0)) {
            WebProperty[] props = storeBean.getWebProperty();
            for (int i = 0; i < props.length; i++) {

                String name = props[i].getAttributeValue(WebProperty.NAME);
                String value = props[i].getAttributeValue(WebProperty.VALUE);
                if (name == null || value == null) {
                    throw new IllegalArgumentException(
                        _rb.getString("webcontainer.nullWebProperty"));
                }

                if (name.equalsIgnoreCase("persistenceScope")) {
                    persistenceScope = value;
                    break;
                }
            }
        }
        return persistenceScope;
    }
        
    
    /**
     * Configure the attributes of the session, such as the timeout,
     * whether to force URL rewriting etc.
     * HERCULES:mod passing in new param wbd
     */
    private void configureSession(WebModule ctx, SessionProperties spBean,
                                  WebBundleDescriptor wbd) {

        boolean timeoutConfigured = false;
        int timeoutSeconds = 1800; // tomcat default (see StandardContext)
        boolean enableCookies = true;

        if ((spBean != null) && (spBean.sizeWebProperty() > 0)) {
            WebProperty[] props = spBean.getWebProperty();
            for (int i = 0; i < props.length; i++) {

                String name = props[i].getAttributeValue(WebProperty.NAME);
                String value = props[i].getAttributeValue(WebProperty.VALUE);
                if (name == null || value == null) {
                    throw new IllegalArgumentException(
                        _rb.getString("webcontainer.nullWebProperty"));
                }

                if (name.equalsIgnoreCase("timeoutSeconds")) {
                    try {
                        timeoutSeconds = Integer.parseInt(value);
                        timeoutConfigured = true;
                    } catch (NumberFormatException e) {
                        // XXX need error message
                    }
                } else if (name.equalsIgnoreCase("enableCookies")) {
                    enableCookies = ConfigBean.toBoolean(value);
                } else {
                    Object[] params = { name };
                    _logger.log(Level.INFO, "webcontainer.notYet", params);
                }
            }
        }
        
        //HERCULES:added/modified
        int webXmlTimeoutSeconds = -1; 
        if(wbd != null) {
            webXmlTimeoutSeconds = wbd.getSessionTimeout() * 60;
        }

        //web.xml setting has precedence if it exists
        //ignore if the value is the 30 min default
        if (webXmlTimeoutSeconds != -1 && webXmlTimeoutSeconds != 1800) {
            ctx.getManager().setMaxInactiveIntervalSeconds(webXmlTimeoutSeconds);
        } else {
            /*
             * Do not override Tomcat default, unless 'timeoutSeconds' was
             * specified in sun-web.xml
             */
            if (timeoutConfigured) {
                ctx.getManager().setMaxInactiveIntervalSeconds(timeoutSeconds);
            }
            
        }
        //end HERCULES:added/modified

        ctx.setCookies(enableCookies);
    }        

    /**
     * Configure the settings for the session cookie using the values
     * in sun-web.xml's cookie-property
     */
    private void configureCookieProperties(WebModule ctx,
                                           CookieProperties bean) {
        if (bean != null) {
            WebProperty[] props = bean.getWebProperty();
            if (props != null) {
                SessionCookieConfig cookieConfig = new SessionCookieConfig();
                for (int i = 0; i < props.length; i++) {

                    String name = props[i].getAttributeValue(WebProperty.NAME);
                    String value = props[i].getAttributeValue(WebProperty.VALUE);
                    if (name == null || value == null) {
                        throw new IllegalArgumentException(
                            _rb.getString("webcontainer.nullWebProperty"));
                    }

                    if (name.equalsIgnoreCase("cookieName")) {
                        cookieConfig.setName(value);
                    } else if (name.equalsIgnoreCase("cookiePath")) {
                        cookieConfig.setPath(value);
                    } else if (name.equalsIgnoreCase("cookieMaxAgeSeconds")) {
                        try {
                            cookieConfig.setMaxAge(Integer.parseInt(value));
                        } catch (NumberFormatException e) {
                            // XXX need error message
                        }
                    } else if (name.equalsIgnoreCase("cookieDomain")) {
                        cookieConfig.setDomain(value);
                    } else if (name.equalsIgnoreCase("cookieComment")) {
                        cookieConfig.setComment(value);
                    } else {
                        Object[] params = { name, value };
                        _logger.log(Level.WARNING,
                                    "webcontainer.invalidProperty",
                                    params);
                    }
                }
                if (props.length > 0) {
                    if (_logger.isLoggable(Level.FINE)) {
                        _logger.fine("WebModule[" + ctx.getPath() + "]: "
                                     + cookieConfig);
                    }
                    ctx.setSessionCookieConfig(cookieConfig);
                }
            }
        }
    }


    /**
     * Configures the given <code>VirtualServer</code> with its state
     * (on | off | disabled).
     */
    protected void configureVirtualServerState(
            VirtualServer host,
            com.sun.enterprise.config.serverbeans.VirtualServer vsBean){

        String stateValue = ON;
        if (vsBean != null){
            stateValue = vsBean.getState();
        }

        if ( ( !stateValue.equalsIgnoreCase(ON) )
            && ( host.getName().equalsIgnoreCase(ADMIN_VS) ) ){
            throw new java.lang.IllegalArgumentException(
                "virtual-server " 
                + ADMIN_VS + " state property cannot be modified");
        }
        
        if ( stateValue.equalsIgnoreCase(DISABLED) ) {
            // state="disabled"
            host.setIsDisabled(true);
        } else if ( !ConfigBean.toBoolean( stateValue ) ) {
            // state="off"
            host.setIsOff(true);
        }
    }


    /**
     * Configures the SSO valve for the given virtual server from the
     * SSO configuration specified in the given virtual server bean.
     */
    private void configureSSOValve(
            VirtualServer vs,
            com.sun.enterprise.config.serverbeans.VirtualServer vsBean) {

        if (vsBean == null) {
            return;
        }

        // is SSO enabled?
        if (!isSSOEnabled(vsBean)) {
            Object[] params = {  vs.getID() };
            _logger.log(Level.INFO, "webcontainer.ssodisabled", params);

        } else {
            // yes, configure and add SSO valve
            try {
                WebContainerFeatureFactory webContainerFeatureFactory =
                    _serverContext.getPluggableFeatureFactory().getWebContainerFeatureFactory();
                SSOFactory ssoFactory = webContainerFeatureFactory.getSSOFactory();
                SingleSignOn sso = ssoFactory.createSingleSignOnValve();
                if (_debug > 1)
                    sso.setDebug(_debug);
            
                // set max idle time if given
                ElementProperty idle =
                    vsBean.getElementPropertyByName(SSO_MAX_IDLE);
                if (idle !=null && idle.getValue() != null) {
                    _logger.fine("SSO entry max idle time set to: " +
                                 idle.getValue());
                    int i = Integer.parseInt(idle.getValue());
                    sso.setMaxInactive(i);
                }

                // set expirer thread sleep time if given
                ElementProperty expireTime =
                    vsBean.getElementPropertyByName(SSO_REAP_INTERVAL);
                if (expireTime !=null && expireTime.getValue() != null) {
                    _logger.fine("SSO expire thread interval set to : " +
                                 expireTime.getValue());
                    int i = Integer.parseInt(expireTime.getValue());
                    sso.setReapInterval(i);
                }

                vs.addValve(sso);

            } catch (Exception e) {
                _logger.log(Level.WARNING, "webcontainer.ssobadconfig", e);
                _logger.log(Level.WARNING, "webcontainer.ssodisabled",
                            vs.getID());
            }
        }
    }


    /**
     * Configures the Remote Address Filter valve for the given virtual
     * server.
     *
     * This valve enforces request accpetance/denial based on the string
     * representation of the remote client's IP address.
     */
    public void configureRemoteAddressFilterValve(
            VirtualServer vs,
            com.sun.enterprise.config.serverbeans.VirtualServer vsBean) {

        RemoteAddrValve remoteAddrValve = null;

        if (vsBean == null) {
            return;
        }

        ElementProperty allow = vsBean.getElementPropertyByName(
            "allowRemoteAddress");
        ElementProperty deny = vsBean.getElementPropertyByName(
            "denyRemoteAddress");
        if ((allow != null && allow.getValue() != null)
                || (deny != null && deny.getValue() != null))  {
            remoteAddrValve = new RemoteAddrValve();
        }

        if (allow != null && allow.getValue() != null) {
            _logger.fine("Allowing access to " + vs.getID()+ " from " +
                         allow.getValue());
            remoteAddrValve.setAllow(allow.getValue());
        }

        if (deny != null && deny.getValue() != null) {
            _logger.fine("Denying access to " + vs.getID()+ " from " +
                         deny.getValue());
            remoteAddrValve.setDeny(deny.getValue());
        }

        if (remoteAddrValve != null) {
            // Remove existing RemoteAddrValve (if any), in case of a reconfig
            Valve[] valves = vs.getValves();
            for (int i=0; valves!=null && i<valves.length; i++) {
                if (valves[i] instanceof RemoteAddrValve) {
                    vs.removeValve(valves[i]);
                    break;
                }
            }
            vs.addValve(remoteAddrValve);
        }
    }


    /**
     * Configures the Remote Host Filter valve for the given virtual
     * server.
     * 
     * This valve enforces request acceptance/denial based on the name of the
     * remote host from where the request originated.
     */
    public void configureRemoteHostFilterValve(
            VirtualServer vs,
            com.sun.enterprise.config.serverbeans.VirtualServer vsBean,
            HttpProtocol httpProtocol) {

        RemoteHostValve remoteHostValve = null;

        if (vsBean == null) {
            return;
        }

        ElementProperty allow = vsBean.getElementPropertyByName(
            "allowRemoteHost");
        ElementProperty deny = vsBean.getElementPropertyByName(
            "denyRemoteHost");
        if ((allow != null && allow.getValue() != null)
                || (deny != null && deny.getValue() != null))  {
            remoteHostValve = new RemoteHostValve();
        }

        if (allow != null && allow.getValue() != null) {
            _logger.fine("Allowing access to " + vs.getID()+ " from " +
                         allow.getValue());
            if (httpProtocol == null || !httpProtocol.isDnsLookupEnabled()) {
                _logger.log(Level.WARNING,
                            "webcontainer.allowRemoteHost.dnsLookupDisabled",
                            vs.getID());
            }
            remoteHostValve.setAllow(allow.getValue());
        }

        if (deny != null && deny.getValue() != null) {
            _logger.fine("Denying access to " + vs.getID()+ " from " +
                         deny.getValue());
            if (httpProtocol == null || !httpProtocol.isDnsLookupEnabled()) {
                _logger.log(Level.WARNING,
                            "webcontainer.denyRemoteHost.dnsLookupDisabled",
                            vs.getID());
            }
            remoteHostValve.setDeny(deny.getValue());
        }

        if (remoteHostValve != null) {
            // Remove existing RemoteHostValve (if any), in case of a reconfig
            Valve[] valves = vs.getValves();
            for (int i=0; valves!=null && i<valves.length; i++) {
                if (valves[i] instanceof RemoteHostValve) {
                    vs.removeValve(valves[i]);
                    break;
                }
            }
            vs.addValve(remoteHostValve);
        }
    }


    /**
     * Undeploy a web application.
     *
     * @param contextRoot the context's name to undeploy
     * @param appName the J2EE appname used at deployment time
     * @param virtualServers List of current virtual-server object.
     * @param wbd Web bundle descriptor, or null if dummy context
     *        (see loadWebModule())
     */
    public void unloadWebModule(String contextRoot,
                                String appName,
                                String virtualServers,
                                WebBundleDescriptor wbd) {
        disableWSMonitoring(wbd);

        if (_logger.isLoggable(Level.FINEST)) {
            _logger.finest("WebContainer.unloadWebModule(): contextRoot: "
                           + contextRoot + " appName:" + appName);
        }

        //Hercules:add
        SessionPurgeUtil purgeUtil = new SessionPurgeUtil(_embedded);
        //end: Hercules:add                                     
                                    
        cleanSecurityContext(appName);

        // unregister WebBundleDescriptor from the WebModulesManager
        if (webModulesManager != null) {
            webModulesManager.unregisterDescriptor(appName);
        }
        
        // tomcat contextRoot starts with "/"
        if (!contextRoot.equals("") && !contextRoot.startsWith("/") ) {
            contextRoot = "/" + contextRoot;
        } else if ("/".equals(contextRoot)) {
            // Make corresponding change as in WebModuleConfig.getContextPath()
            contextRoot = "";
        }
        
        Engine[] engines = _embedded.getEngines();
        List hostList = StringUtils.parseStringList(virtualServers, " ,");
        boolean unloadToAll = (hostList == null) || (hostList.size() == 0);
        boolean hasBeenUndeployed = false;
        Container[] hostArray = null;
        VirtualServer host = null;
        WebModule context = null;
        
        for (int j = 0; j < engines.length; j++) {
            hostArray = engines[j].findChildren();
            for (int i = 0; i < hostArray.length; i++) {
                host = (VirtualServer) hostArray[i];

                /**
                 * Related to Bug: 4904290
                 * Do not unloadload module on ADMIN_VS
                 */
                if ( unloadToAll && host.getName().equalsIgnoreCase(ADMIN_VS)){
                    continue;
                }

                if (unloadToAll 
                        || hostList.contains(host.getName())
                        || verifyAlias(hostList,host)){

                    context = (WebModule) host.findChild(contextRoot);
                    if (context != null) {
                        //Hercules:add
                        if (_logger.isLoggable(Level.FINEST)) {
                            _logger.finest("about to call SessionPurgeUtil "
                                           + "for " + appName
                                           + " contextRoot: " + contextRoot);
                        }
                        purgeUtil.purgeSessionsForContext(context); 
                        //end: Hercules:add
                        disableMonitoring(context, host.getName());
                        host.removeChild(context);
                        try {
                            ((StandardContext)context).destroy();
                        } catch (Exception ex) {
                            _logger.log(Level.WARNING,
                                "[WebContainer] Context " + contextRoot
                                + " threw exception in destroy()", ex);
                        }
                        // START SJSAS 6330332
                        Switch sw = Switch.getSwitch();
                        sw.removeDescriptorFor(context);
                        // END SJSAS 6330332
                        if (_logger.isLoggable(Level.FINEST)) {
                            _logger.log(Level.FINEST,
                                "[WebContainer] Context " + contextRoot
                                + " undeployed from " + host);
                        }
                        hasBeenUndeployed = true;
                        ((StandardHost)host).fireContainerEvent(
                            Deployer.REMOVE_EVENT, context);
                        /*
                         * If the web module that has been unloaded
                         * contained any mappings for ad-hoc paths,
                         * those mappings must be preserved by registering an
                         * ad-hoc web module at the same context root
                         */
                        if (context.hasAdHocPaths()
                                || context.hasAdHocSubtrees()) {
                            WebModule wm = createAdHocWebModule(
                                            host,
                                            contextRoot,
                                            context.getJ2EEApplication());
                            wm.addAdHocPaths(context.getAdHocPaths());
                            wm.addAdHocSubtrees(context.getAdHocSubtrees());
                        }
                    }
                }
            }
        }   
        

        if (!hasBeenUndeployed) {
            _logger.log(Level.SEVERE,
                "[WebContainer] Undeployment failed for context "
                + contextRoot);
        }        
        
    }  
    
    
    /**
     * Creates and configures a web module for each virtual server
     * that the web module is hosted under. If the web module has been
     * disabled by a call to disableWebModule, enable the module
     * instead of re-crearing new one.
     *
     * If no virtual servers are specified, then the web module is
     * enabled` on EVERY virtual server.
     */
    public void enableWebModule(WebModuleConfig wmInfo,
                                String j2eeApplication){ 
        String vsIDs = wmInfo.getVirtualServers();
        List vsList = StringUtils.parseStringList(vsIDs, " ,");
        boolean enabledToAll = (vsList == null) || (vsList.size() == 0);

        Engine[] engines =  _embedded.getEngines();

        for (int j=0; j<engines.length; j++) {
            Container[] vsArray = engines[j].findChildren();
            for (int i = 0; i < vsArray.length; i++) {
                if (vsArray[i] instanceof VirtualServer) {
                    VirtualServer vs = (VirtualServer) vsArray[i];

                    /*
                     * Fix for 4913636: If vsList is null and virtual server is
                     * equal to __asadmin, continue with next iteration
		     * because we don't want to load user apps on __asadmin
                     */
                    if (vs.getID().equals(ADMIN_VS) && enabledToAll) {
                        continue;
                    }

                    if ( enabledToAll 
                            || vsList.contains(vs.getID()) 
                            || verifyAlias(vsList,vs)){

                        loadWebModule(vs, wmInfo, j2eeApplication);
                    }
                }
            }
        }
    }


    /**
     * Disable a web application.
     * @param contextRoot the context's name to undeploy
     * @param appName the J2EE appname used at deployment time
     * @param virtualServers the list of current virtual-server object.
     */
    public void disableWebModule(String contextRoot,
                                 String appName,
                                 String virtualServers){
                                    
        // tomcat contextRoot starts with "/"
        if (!contextRoot.equals("") && !contextRoot.startsWith("/") ) {
            contextRoot = "/" + contextRoot;
        }
        
        Engine[] engines = _embedded.getEngines();
        List hostList = StringUtils.parseStringList(virtualServers, " ,");
        boolean disableToAll = (hostList == null) || (hostList.size() == 0);
        boolean hasBeenDisabled = false;
        Container[] hostArray = null;
        VirtualServer host = null;
        Context context = null;
        
        for (int j = 0; j < engines.length; j++) {
            hostArray = engines[j].findChildren();
            for (int i = 0; i < hostArray.length; i++) {
                host = (VirtualServer) hostArray[i];

                /**
                 * Related to Bug: 4904290
                 * Do not unloadload module on ADMIN_VS
                 */
                if ( disableToAll 
                        && host.getName().equalsIgnoreCase(ADMIN_VS)){
                    continue;
                }

                if (disableToAll 
                        || hostList.contains(host.getName())
                        || verifyAlias(hostList,host)){

                    context = (Context) host.findChild(contextRoot);
                    if (context != null) {
                        context.setAvailable(false);
                        if (_logger.isLoggable(Level.FINEST)) {
                            _logger.log(Level.FINEST,
                                        "[WebContainer] Context "
                                        + contextRoot + " disabled from "
                                        + host);
                        }
                        hasBeenDisabled = true;
                    }
                }
            }
        }   
        
        if (!hasBeenDisabled){
            _logger.log(Level.WARNING,
                        "[WebContainer] moduleDisabled fail for context "
                        + contextRoot);
        }        
        
    }  


    /**
     * Save the server-wide dynamic reloading settings for use when
     * configuring each web module.
     */
    private void getDynamicReloadingSettings(DasConfig appsBean) {
        if (appsBean != null) {
            _reloadingEnabled = appsBean.isDynamicReloadEnabled();
            String seconds = appsBean.getDynamicReloadPollIntervalInSeconds();
            if (seconds != null) {
                try {
                    _pollInterval = Integer.parseInt(seconds);
                } catch (NumberFormatException e) {
                }
            }
        }
    }

    /**
     * Set the level for the web container logger and determine the
     * debug setting for Catalina's containers based on the iAS log
     * level.
     */
    private void setLogLevel(Level level) {
        _logLevel = level;
        _logger.setLevel(_logLevel);

        // Determine the appropriate value for Catalina's debug level
        if (level.equals(Level.FINE))
            _debug = 1;
        else if (level.equals(Level.FINER))
            _debug = 2;
        else if (level.equals(Level.FINEST))
            _debug = 5;
        else
            _debug = 0;
    }


   /*
    * Initializes the web container log level from the domain.xml.
    *
    * @param logserviceBean The log service bean from which to retrieve the
    *        web container log level
    */
    private void initLogLevel(LogService logserviceBean)
            throws ConfigException {

        Level level = Level.SEVERE;
        setLogLevel(level);

        if (logserviceBean != null) {
            try {
                level = Level.parse(logserviceBean.getModuleLogLevels().getRoot());
                setLogLevel(level);
            } catch (NullPointerException e) {
            } catch (IllegalArgumentException e) { }
        }

        // If the <web-container> element in server.xml contains a 
        // log-level setting then use that
        try {
            level = Level.parse(ServerBeansFactory.getWebContainerLogLevel(
                                                            _configContext));
            setLogLevel(level);
        } catch (NullPointerException e) {
            if (_debug > 0)
                _logger.finest("Defaulting <web-container> log-level");
        } catch (IllegalArgumentException e) {
        }
        if (_debug > 0) {
            _logger.fine("Web container log level: " + _logLevel);
        }
    }


   /*
    * Initializes the web container monitoring level from the domain.xml.
    *
    * @param monitoringBean The monitoring service bean from which to retrieve
    *        the web container monitoring level
    */
    private void initMonitoringLevel(MonitoringService monitoringBean) {

        monitoringLevel = MonitoringLevel.OFF; // default per DTD

        if (monitoringBean != null) {
            ModuleMonitoringLevels levels =
                monitoringBean.getModuleMonitoringLevels();
            if (levels != null) {
                monitoringLevel = MonitoringLevel.instance(
                                                levels.getWebContainer());
            }
        }
    }


    /**
     * Checks if SSO is enabled for this virtual server.
     *
     * @param The virtual server id
     *
     * @return The value of the sso-enabled property for this virtual server
     */
    private boolean isSSOEnabled(com.sun.enterprise.config.serverbeans.VirtualServer vsBean)
    {
        ElementProperty ssoProperty  = 
            vsBean.getElementPropertyByName(SSO_ENABLED);

        if (ssoProperty!=null && ssoProperty.getValue()!=null 
             && !ConfigBean.toBoolean( ssoProperty.getValue())) {
           return false;
        }
        return true;
    }


    /**
     * Gets the VirtualServer config bean corresponding to the given id.
     *
     * @param vsID The virtual server id
     *
     * @return The VirtualServer config bean corresponding to the given id, or
     * null if no match is found
     */
    private com.sun.enterprise.config.serverbeans.VirtualServer findVirtualServerBean(String vsID) {

        HttpService httpService = null;
        try {
            httpService = ServerBeansFactory.getHttpServiceBean(_configContext);
        } catch (ConfigException e) {
            String msg = _rb.getString("webcontainer.httpConfigError");
            _logger.log(Level.SEVERE, msg, e);
        }

        com.sun.enterprise.config.serverbeans.VirtualServer[] vses
            = httpService.getVirtualServer();
        for (int i=0; vses!=null && i<vses.length; i++) {
            if (vsID.equals(vses[i].getId())) {
                return vses[i];
            }
        }

        return null;
    }

    
    /** 
     * Get the lifecycle listeners associated with this lifecycle. If this
     * Lifecycle has no listeners registered, a zero-length array is returned.
     */
    public LifecycleListener[] findLifecycleListeners() {
       return new LifecycleListener[0];
    }
    
    
    /**
     * Clean security policy generated at deployment time.
     * NOTE: This routine calls destroy on the WebSecurityManagers, 
     * but that does not cause deletion of the underlying policy (files).
     * The underlying policy is deleted when removePolicy 
     * (in AppDeployerBase and WebModuleDeployer) is called.
     * @param appName  the app name
     */
    private void cleanSecurityContext(String appName) {
	String cIDs[] = webSecurityManagerFactory.getContextIdsOfApp(appName);

	for (int i=0; cIDs != null && i <cIDs.length; i++) {
	    WebSecurityManager wsm
		= webSecurityManagerFactory.getWebSecurityManager(cIDs[i]);

	    _logger.log(Level.FINE,"[JACC]: Removing WebSecurityManager: "
                + cIDs[i]);

	    if (wsm != null) {
		try {
		    wsm.destroy();
		} catch (javax.security.jacc.PolicyContextException pce){
		    // log it and continue
		    _logger.log(Level.WARNING,
                            "Unable to destroy WebSecurityManager",
                            pce);
		}
		webSecurityManagerFactory.removeWebSecurityManager(cIDs[i]);
	    }
	}
    }
           
    /**
     * Virtual servers are maintained in the reference contained 
     * in Server element. First, we need to find the server
     * and then get the virtual server from the correct reference
     *
     * @param appName Name of the app to get vs
     * @param configCtx config context to use to obtain virtual servers. If
     *     this parameter is null, config context cached at startup time will
     *     be used.
     *
     * @return virtual servers as a string (separated by space or comma)
     */
    private String getVirtualServers(String appName, ConfigContext configCtx) {
        if (configCtx == null) {
            configCtx = _configContext;
        }
        String ret = null;
        try {
            ret = ServerBeansFactory.getVirtualServersByAppName(configCtx,
                                                                appName);
        } catch (ConfigException ce) {
            _logger.log(Level.WARNING,
                        "No virtual servers defined for " + appName,
                        ce);
        }
        return ret;
    }

    /**
     * Adds the given mime mappings to those of the specified context, unless
     * they're already present in the context (that is, the mime mappings of
     * the specified context, which correspond to those in default-web.xml,
     * can't be overridden).
     *
     * @param ctx The StandardContext to whose mime mappings to add
     * @param mimeMap The mime mappings to be added
     */
    private void addMimeMappings(StandardContext ctx, MimeMap mimeMap) {
        if (mimeMap == null) {
            return;
        }

        for (Iterator itr = mimeMap.getExtensions(); itr.hasNext(); ) {
            String extension = (String) itr.next();
            if (ctx.findMimeMapping(extension) == null) {
                ctx.addMimeMapping(extension, mimeMap.getType(extension));
            }
        }
    }
    
    /**
     * Enables monitoring of web service endpoints in for all webmodules.
     */
    private void enableAllWSEndpoints() {

        Applications appsBean = null;
        try {
            appsBean = ServerBeansFactory.getApplicationsBean(_configContext);
        } catch (ConfigException e) {
            String msg = _rb.getString("webcontainer.appsConfigError");
            _logger.log(Level.SEVERE, msg, e);
        }

        if (appsBean != null) {
            J2eeApplication[] j2eeAppBeans = appsBean.getJ2eeApplication();
            if (j2eeAppBeans != null) {
                for (int i = 0; i < j2eeAppBeans.length; i++) {
                    //Begin EE: 4927099 - load only associated applications
                    if ( isReferenced(j2eeAppBeans[i].getName()) ) {
                        enableWSMonitoring(j2eeAppBeans[i].getName());
                    }
                    //End EE: 4927099 - load only associated applications
                }
            }
        }
    }
 
     /**
      * Disables monitoring of web service endpoints in a webmodule.
      *
      * @param wbd WebBundleDescriptor of the web module
      *
      * @return boolean true, if web service mbeans were unloaded successfully
      */
     private boolean disableWSMonitoring(WebBundleDescriptor wbd)    {

       boolean result = true;

            try {
            Switch.getSwitch().getManagementObjectManager().
                deleteWSEndpointMBeans(wbd, instance.getName());
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
            return true;
    }

    /**
     * Enables monitoring of web service endpoints in a webmodule.
     *
     * @param id ID of the application
     *
     * @return boolean true, if web service mbeans were intialized successfully
     */
    private boolean enableWSMonitoring(String id)    {

       boolean result = true;

       ApplicationRegistry registry = ApplicationRegistry.getInstance();
            ClassLoader appLoader = registry.getClassLoaderForApplication(id);
            if (appLoader != null) {
                Application appDesc = registry.getApplication(appLoader); 
             
                // Check to see if this app had deployed successfully (4663247)
                if(appDesc == null){ 
                    Object[] params = { id };
                    _logger.log(Level.SEVERE, "webcontainer.notLoaded",
                                params);
                } else {
                    //end Hercules: add                     
                    String j2eeApplication = appDesc.getRegistrationName();
                    Set wbds = appDesc.getWebBundleDescriptors();
                    WebBundleDescriptor wbd = null;
                    for (Iterator itr = wbds.iterator(); itr.hasNext(); ) {
                        wbd = (WebBundleDescriptor) itr.next();

                        try {
                            Switch.getSwitch().getManagementObjectManager().
                            createWSEndpointMBeans(wbd, instance.getName());
                        } catch (Exception e) {
                            e.printStackTrace();
                            return false;
                        }
                    }
                }
            }
        return true;
    }

    void enableWSMonitoring(WebBundleDescriptor wbd, String serverName) {
         try {
             Switch.getSwitch().getManagementObjectManager().
             createWSEndpointMBeans(wbd, serverName);
         } catch (Exception e) {
             e.printStackTrace();
         }
     }

    /**
     * Enables monitoring on the Servlets in a webmodule.
     *
     * @param ctx Web module to be monitored
     * @param vsId  The engine that is currently loading the webmodule
     */
    void enableMonitoring(WebModule ctx, String vsId) {

        if (!ctx.hasWebXml()) {
            // dummy web module
            return;
        }

        String j2eeServer = _serverContext.getInstanceName();

        // Register web module stats
        registerWebModuleStats(ctx.getJ2EEApplication(), j2eeServer, vsId,
                               ctx, null);

        // Register stats for each of the web module's servlets
        Container[] children = ctx.findChildren();
        if (children != null) {
            for (int i = 0; i < children.length; i++) {
                registerServletStats(ctx.getJ2EEApplication(), j2eeServer,
                                     ctx.getModuleName(), vsId, ctx.getPath(),
                                     children[i].getName(), null);
            }
        }
    }

    
    /**
     * Disables monitoring on the given web module.
     */
    protected void disableMonitoring(WebModule ctx, String vsId) {

        if (!ctx.hasWebXml()) {
            // dummy web module
            return;
        }

        /*
         * Standalone webmodules are loaded with the application name set to
         * the string "null"
         */
        String appName = ctx.getJ2EEApplication();
        if ("null".equalsIgnoreCase(appName)) {
            appName = null;
        }

        // Unregister stats for each of the web module's servlets
        Container[] children = ctx.findChildren();
        if (children != null) {
            for (int k = 0; k < children.length; k++) {
                unregisterServletStats(appName, ctx.getModuleName(),
                                       ctx.getPath(), vsId,
                                       children[k].getName());
            }
        }
        // Unregister web module stats
        unregisterWebModuleStats(appName, ctx.getModuleName(),
                                 ctx.getPath(), vsId);

    }

    /*
     * Excludes, from TLD scanning, any JARs on the classpath and higher up in
     * the classloader delegation chain that are known not to contain any TLDs
     * or TLD listeners.
     */
    private void setNoTldScan() {
        boolean first = true;
        StringBuffer sb = new StringBuffer();
        StringBuffer sbListener = new StringBuffer();
        List tldJars = null;
        List tldListeners = null;
        
        String prop = System.getProperty("com.sun.enterprise.taglibs");
        if (prop != null) {
            tldJars = StringUtils.parseStringList(prop, ",");
        }
        
        prop = System.getProperty("com.sun.enterprise.taglisteners");
        if (prop != null) {
            tldListeners = StringUtils.parseStringList(prop, ",");
        }  
        
        // Check to see if we need to scan the parent classloader when
        // searching for TLD listener. JSF application mandate the search, as
        // well as shared TLD added to the 
        // property com.sun.enterprise.taglisteners
        if ( tldListeners != null ){
            int size = tldListeners.size();
            if ( size > 0 ){
                // By default, domain.xml contains 1 elements.
                if ( !tldListeners.contains("jsf-impl.jar") || (size > 1))
                    TldConfig.setScanParentTldListener(true);
            }
        }

        ClassLoader loader = getClass().getClassLoader();
        while (loader != null) {
            if (loader instanceof URLClassLoader) {
                URL[] urls = ((URLClassLoader) loader).getURLs();
                for (int i=0; i<urls.length; i++) {
                    String url = urls[i].toString();
                    int index = url.lastIndexOf('/');
                    if (index != -1) {
                        url = url.substring(index+1);
                    }
                    if (url != null && url.endsWith(".jar")
  			    && (tldJars == null || !tldJars.contains(url))
                            
                            ) {
                        if (first) {
                            sb.append(url);
                            first = false;
                        } else {
                            sb.append(',');
                            sb.append(url);
                        }
                    }
                    
                    if (url != null && url.endsWith(".jar")
  			    && (tldListeners == null || !tldListeners.contains(url))
                            
                            ) {
                        if (first) {
                            sbListener.append(url);
                            first = false;
                        } else {
                            sbListener.append(',');
                            sbListener.append(url);
                        }
                    }
                }
            }
            loader = loader.getParent();
        }

        if (sbListener.length() > 0) {
            TldConfig.setNoTldListeners(sbListener.toString());
        }
        
        if (sb.length() > 0) {
            TldLocationsCache.setNoTldJars(sb.toString());
        }
    }
    

    // Begin EE: 4927099 load only associated applications
    /**
     * Returns true if the names application is referenced by this server.
     *
     * @param  appName  name of the application id
     * @return true if the names application is referenced by this server
     */
    private boolean isReferenced(String appName) {

        Servers servers = domain.getServers();
        Server server = servers.getServerByName(
                                _serverContext.getInstanceName());

        ApplicationRef ref = server.getApplicationRefByRef(appName);
        return (ref == null) ? false : true;
    }
    // End EE: 4927099 load only associated applications


    /*
     * Creates a ServletStats instance for the servlet with the given
     * <code>servletName</code> and registers it with the monitoring registry.
     *
     * @param j2eeApplication String representing the J2EE Application to which
     *        the web module belongs, or null if the web module is stand-alone
     * @param j2eeServer The server instance name
     * @param webModuleName The name of the web module to which the servlet
     *        belongs
     * @param vsId The id of the virtual server on which the web module has
     *        been deployed
     * @param contextRoot Context root at which web module has been deployed
     * @param servletName The servlet name
     * @param listener The listener for monitoring level changes
     */
    private void registerServletStats(String j2eeApplication,
                                      String j2eeServer,
                                      String webModuleName,
                                      String vsId,
                                      String contextRoot,
                                      String servletName,
                                      MonitoringLevelListener listener) {

        PwcServletStats pwcServletStats = new PwcServletStatsImpl(
                                    _serverContext.getDefaultDomainName(), 
                                    vsId, contextRoot, servletName,
                                    j2eeApplication, j2eeServer);
        ServletStats servletStats = new ServletStatsImpl(pwcServletStats);


        MonitoringRegistry monitoringRegistry =
                _serverContext.getMonitoringRegistry();

        /*
         * Standalone webmodules are loaded with the application name set to
         * the string "null"
         */
        String app = ("null".equalsIgnoreCase(j2eeApplication) ?
                        null : j2eeApplication);
        try {
            monitoringRegistry.registerServletStats(servletStats,
                                                    app,
                                                    webModuleName,
                                                    contextRoot,
                                                    vsId,
                                                    servletName,
                                                    listener);
        } catch(Exception e) {
            _logger.log(Level.WARNING,
                        "Exception during registration of servletstats",
                        e);
        }
    }


    /*
     * Removes the ServletStats instance associated with the servlet with the
     * given <code>servletName</code> from the monitoring registry.
     *
     * @param j2eeApplication String representing the J2EE Application to which
     *        the web module belongs, or null if the web module is stand-alone
     * @param webModuleName The name of the web module to which the servlet
     *        belongs
     * @param contextRoot Context root at which web module has been deployed
     * @param vsId The id of the virtual server on which the web module has
     *        been deployed
     * @param servletName The servlet name
     */
    private void unregisterServletStats(String j2eeApplication,
                                        String webModuleName,
                                        String contextRoot,
                                        String vsId,
                                        String servletName) {

        MonitoringRegistry monitoringRegistry =
                _serverContext.getMonitoringRegistry();

        try {
            monitoringRegistry.unregisterServletStats(j2eeApplication,
                                                      webModuleName,
                                                      contextRoot,
                                                      vsId,
                                                      servletName);
        } catch (Exception e) {
            _logger.log(Level.WARNING,
                        "Exception during unregistration of servletstats",
                        e);
        }
    }


    /*
     * Creates a WebModuleStats instance for the given web module and
     * registers it with the monitoring registry.
     *
     * @param j2eeApplication String representing the J2EE Application to which
     *        the web module belongs, or null if the web module is stand-alone
     * @param j2eeServer The server instance name
     * @param vsId The id of the virtual server on which the web module has
     *        been deployed
     * @param ctx The web module
     * @param listener The listener for monitoring level changes
     *
     * @return The WebModuleStats that was registered with the monitoring
     * registry
     */
    private WebModuleStats registerWebModuleStats(
                                        String j2eeApplication,
                                        String j2eeServer,
                                        String vsId,
                                        WebModule ctx,
                                        MonitoringLevelListener listener) {

        WebContainerFeatureFactory webFeatureFac = 
            _serverContext.getPluggableFeatureFactory().getWebContainerFeatureFactory();
        WebModuleStatsImpl webStats = (WebModuleStatsImpl)
            webFeatureFac.getWebModuleStats();
        PwcWebModuleStatsImpl pwcWebStats = new PwcWebModuleStatsImpl(
                                        ctx.getObjectName(),
                                        ctx.getPath(),
                                        _serverContext.getDefaultDomainName(), 
                                        vsId,
                                        j2eeApplication,
                                        j2eeServer);
        webStats.setPwcWebModuleStats(pwcWebStats);
        webStats.setSessionManager(ctx.getManager());

        MonitoringRegistry monitoringRegistry =
                _serverContext.getMonitoringRegistry();

        /*
         * Standalone webmodules are loaded with the application name set to
         * the string "null"
         */
        String app = ("null".equalsIgnoreCase(j2eeApplication) ?
                        null : j2eeApplication);
        try {
            monitoringRegistry.registerWebModuleStats(webStats,
                                                      app,
                                                      ctx.getModuleName(),
                                                      ctx.getPath(),
                                                      vsId,
                                                      listener);
        } catch (Exception e) {
            _logger.log(Level.WARNING,
                        "Fail to register WebModuleStats for "
                        + ctx.getModuleName() + " deployed on " + vsId, e);
        }

        return webStats;
    }


    /*
     * Removes the WebModuleStats instance associated with the web module with
     * the given <code>webModuleName</code> from the monitoring registry.
     *
     * @param j2eeApplication String representing the J2EE Application to which
     *        the web module belongs, or null if the web module is stand-alone
     * @param webModuleName The web module name
     * @param contextRoot Context root at which web module has been deployed
     * @param vsId The id of the virtual server on which the web module has
     *        been deployed
     */
    private void unregisterWebModuleStats(String j2eeApplication,
                                          String webModuleName,
                                          String contextRoot,
                                          String vsId) { 

        MonitoringRegistry monitoringRegistry =
                _serverContext.getMonitoringRegistry();

        try {
            monitoringRegistry.unregisterWebModuleStats(j2eeApplication,
                                                        webModuleName,
                                                        contextRoot,
                                                        vsId);
        } catch (Exception e) {
            _logger.log(Level.WARNING,
                        "Fail to unregister WebModuleStats for "
                        + webModuleName + " deployed on " + vsId, e);
        }
    }
    
    /**
     * Invoked when a  reference is created from a
     * server instance (or cluster) to a particular module.
     *
     * @throws AdminEventListenerException when the listener is unable to
     *         process the event.
     */
    public void moduleReferenceAdded(ModuleDeployEvent event)
    { //    throws AdminEventListenerException {
                
    }

    /**
     * Invoked when a reference is removed from a
     * server instance (or cluster) to a particular module.
     *
     * @throws AdminEventListenerException when the listener is unable to
     *         process the event.
     */
    public void moduleReferenceRemoved(ModuleDeployEvent event)
    { //     throws AdminEventListenerException {
                
    }
    
    
    /**
     * Invoked when an application reference is created from a
     * server instance (or cluster) to a particular application.
     *
     * @throws AdminEventListenerException when the listener is unable to
     *         process the event.
     */
    public void applicationReferenceAdded(ApplicationDeployEvent event)
    {//       throws AdminEventListenerException {
                
    }

    /**
     * Invoked when a reference is removed from a
     * server instance (or cluster) to a particular application.
     *
     * @throws AdminEventListenerException when the listener is unable to
     *         process the event.
     */
    public void applicationReferenceRemoved(ApplicationDeployEvent event)
    { // throws AdminEventListenerException {
    }
    
    /**
     * Return the current <code>WebContainer</code> child instance.
     */
    public static WebContainer getInstance(){
        return webContainer;
    }
    
    /**
     * Return the parent/top-level container in _embedded for virtual
     * servers.
     */
    public Engine[] getEngines() {
        return _embedded.getEngines();
    }


    /**
     * Registers the given ad-hoc path at the given context root.
     *
     * @param path The ad-hoc path to register
     * @param ctxtRoot The context root at which to register
     * @param appName The name of the application with which the ad-hoc path is
     * associated
     * @param servletInfo Info about the ad-hoc servlet that will service
     * requests on the given path
     */
    public void registerAdHocPath(String path,
                                  String ctxtRoot,
                                  String appName,
                                  AdHocServletInfo servletInfo) {

        registerAdHocPathAndSubtree(path, null, ctxtRoot, appName, servletInfo);
    }


    /**
     * Registers the given ad-hoc path and subtree at the given context root.
     *
     * @param path The ad-hoc path to register
     * @param subtree The ad-hoc subtree path to register  
     * @param ctxtRoot The context root at which to register
     * @param appName The name of the application with which the ad-hoc path
     * and subtree are associated
     * @param servletInfo Info about the ad-hoc servlet that will service
     * requests on the given ad-hoc path and subtree
     */
    public void registerAdHocPathAndSubtree(String path,
                                            String subtree,
                                            String ctxtRoot,
                                            String appName,
                                            AdHocServletInfo servletInfo) {
        WebModule wm = null;

        Engine[] engines =  _embedded.getEngines();
        for (int j=0; j<engines.length; j++) {

            Container[] vsList = engines[j].findChildren();
            for (int i = 0; i < vsList.length; i++) {

                VirtualServer vs = (VirtualServer)vsList[i];
                if (vs.getName().equalsIgnoreCase(ADMIN_VS)) {
                    // Do not deploy on admin vs
                    continue;
                }

                wm = (WebModule) vs.findChild(ctxtRoot);
                if (wm == null) {
                    wm = createAdHocWebModule(vs, ctxtRoot, appName);
                } 
                wm.addAdHocPathAndSubtree(path, subtree, servletInfo);
            }
        }
    }


    /**
     * Unregisters the given ad-hoc path from the given context root.
     *
     * @param path The ad-hoc path to unregister
     * @param ctxtRoot The context root from which to unregister
     */
    public void unregisterAdHocPath(String path, String ctxtRoot) {
        unregisterAdHocPathAndSubtree(path, null, ctxtRoot);
    }


    /**
     * Unregisters the given ad-hoc path and subtree from the given context
     * root.
     *
     * @param path The ad-hoc path to unregister
     * @param subtree The ad-hoc subtree to unregister
     * @param ctxtRoot The context root from which to unregister
     */
    public void unregisterAdHocPathAndSubtree(String path,
                                              String subtree,
                                              String ctxtRoot) {

        WebModule wm = null;

        Engine[] engines =  _embedded.getEngines();
        for (int j=0; j<engines.length; j++) {

            Container[] vsList = engines[j].findChildren();
            for (int i = 0; i < vsList.length; i++) {

                VirtualServer vs = (VirtualServer) vsList[i];
                if (vs.getName().equalsIgnoreCase(ADMIN_VS)) {
                    // Do not undeploy from admin vs, because we never 
                    // deployed onto it
                    continue;
                }

                wm = (WebModule) vs.findChild(ctxtRoot);
                if (wm == null) {
                    continue;
                }

                /*
                 * If the web module was created by the container for the
                 * sole purpose of mapping ad-hoc paths and subtrees,
                 * and does no longer contain any ad-hoc paths or subtrees,
                 * remove the web module.
                 */
                wm.removeAdHocPath(path);
                wm.removeAdHocSubtree(subtree);
                if ((wm instanceof AdHocWebModule)
                        && !wm.hasAdHocPaths()
                        && !wm.hasAdHocSubtrees()) {
                    vs.removeChild(wm);
                    try {
                        wm.destroy();
                    } catch (Exception ex) {
                        _logger.log(Level.WARNING,
                            "[WebContainer] Context " + wm.getPath()
                            + " threw exception in destroy()", ex);
                    }
                }
            }
        }
    }


    /*
     * Creates an ad-hoc web module and registers it on the given virtual
     * server at the given context root.
     *
     * @param vs The virtual server on which to add the ad-hoc web module
     * @param ctxtRoot The context root at which to register the ad-hoc
     * web module
     * @param appName The name of the application to which the ad-hoc module
     * being generated belongs
     *
     * @return The newly created ad-hoc web module
     */
    private WebModule createAdHocWebModule(VirtualServer vs,
                                           String ctxtRoot,
                                           String appName) {

        AdHocWebModule wm = new AdHocWebModule(this);

        wm.restrictedSetPipeline(new WebPipeline(wm));

        // The Parent ClassLoader of the AdhocWebModule was null
        // [System ClassLoader]. With the new hierarchy, the thread context
        // classloader needs to be set.
        if (Boolean.getBoolean(com.sun.enterprise.server.PELaunch.USE_NEW_CLASSLOADER_PROPERTY)) {
            wm.setParentClassLoader(
                Thread.currentThread().getContextClassLoader());
        }

        wm.setContextRoot(ctxtRoot);
        wm.setJ2EEApplication(appName);  
        wm.setName(ctxtRoot);
        wm.setDocBase(vs.getAppBase());
        wm.setEngineName(vs.getParent().getName());

        String domain = _serverContext.getDefaultDomainName();
        wm.setDomain(domain);
  
        String j2eeServer = _serverContext.getInstanceName();
        wm.setJ2EEServer(j2eeServer);

        String server = domain + ":j2eeType=J2EEServer,name=" + j2eeServer;
        wm.setServer(server);

        wm.setCrossContext(true);
        wm.setJavaVMs(J2EEModuleUtil.getjavaVMs());

        vs.addChild(wm);

        return wm;
    }


    /**
     * Adds all libraries specified via the --libraries deployment option to
     * the given loader associated with the given web context.
     *
     * @param loader The loader to configure
     * @param ctx The loader's associated web context
     */
    private void addLibs(Loader loader, WebModule ctx) {
 
        String list = ASClassLoaderUtil.getLibrariesForWebModule(ctx.getID());
        if (list == null) {
            return;
        }
        String[] libs = list.split(",");
        if (libs == null) {
            return;
        }

        File libDir = new File(instance.getLibPath());
        String libDirPath = libDir.getAbsolutePath();
        String appLibsPrefix = libDirPath + File.separator + "applibs"
            + File.separator;

        for (int i=0; i<libs.length; i++) {
            File file = new File(libs[i]);
            if (file.isAbsolute()) {
                loader.addRepository("file:" + file.getAbsolutePath());
            } else {
                loader.addRepository("file:" + appLibsPrefix + libs[i]);
            } 
        }
    }


    /**
     * Configures the given classloader with its attributes specified in
     * sun-web.xml.
     *
     * @param loader The classloader to configure
     * @param clBean The class-loader info from sun-web.xml
     * @param ctx The classloader's associated web module
     */
    private void configureLoaderAttributes(
            Loader loader,
            com.sun.enterprise.deployment.runtime.web.ClassLoader clBean,
            WebModule ctx) {

        String value = null;

        value = clBean.getAttributeValue(
            com.sun.enterprise.deployment.runtime.web.ClassLoader.DELEGATE);

        /*
         * The DOL will *always* return a value: If 'delegate' has not been
         * configured in sun-web.xml, its default value will be returned,
         * which is FALSE in the case of sun-web-app_2_2-0.dtd and
         * sun-web-app_2_3-0.dtd, and TRUE in the case of
         * sun-web-app_2_4-0.dtd.
         */
        boolean delegate = ConfigBean.toBoolean(value);
        loader.setDelegate(delegate);

        // Get any extra paths to be added to the class path of this
        // class loader
        List pathElements = null;
        value = clBean.getAttributeValue(
            com.sun.enterprise.deployment.runtime.web.ClassLoader.EXTRA_CLASS_PATH);
        if (value != null) {
            pathElements = StringUtils.parseStringList(value, ";:");
            if (pathElements != null) {
                ListIterator iter = pathElements.listIterator();
                while (iter.hasNext()) {
                    String path = (String) iter.next();
                    if (_logger.isLoggable(Level.FINE)) {
                        _logger.fine("WebModule[" + ctx.getPath()
                                     + "]: Adding " + path + " to the classpath");
                    }

                    try {
                        URL url = new File(path).toURI().toURL();
                        loader.addRepository(url.toString());
                    } catch (MalformedURLException e) {
                        String msg = _rb.getString("webcontainer.classpathError");
                        Object[] params = { path };
                        msg = MessageFormat.format(msg, params);
                        _logger.log(Level.SEVERE, msg, e);
                    }
                }
            }
        }

        value = clBean.getAttributeValue(
            com.sun.enterprise.deployment.runtime.web.ClassLoader.DYNAMIC_RELOAD_INTERVAL);
        if (value != null) {
            // Log warning if dynamic-reload-interval is specified
            // in sun-web.xml since it is not supported
            _logger.log(Level.WARNING,
                        "webcontainer.dynamicReloadInterval");
        }

        _logger.fine("WebModule[" + ctx.getPath()
             + "]: Setting delegate to " + delegate
             + ", extra-class-path to " + pathElements);
    }


    /**
     * Configures the given classloader with its properties specified in
     * sun-web.xml.
     *
     * @param loader The classloader to configure
     * @param clBean The class-loader info from sun-web.xml
     */
    private void configureLoaderProperties(
            Loader loader,
            com.sun.enterprise.deployment.runtime.web.ClassLoader clBean) {

        String name = null;
        String value = null;

        WebProperty[] props = clBean.getWebProperty();
        if (props == null || props.length == 0) {
            return;
        }

        for (int i = 0; i < props.length; i++) {

            name = props[i].getAttributeValue(WebProperty.NAME);
            value = props[i].getAttributeValue(WebProperty.VALUE);

            if (name == null || value == null) {
                throw new IllegalArgumentException(
                    _rb.getString("webcontainer.nullWebProperty"));
            }

            if (name.equalsIgnoreCase("ignoreHiddenJarFiles")) {
                loader.setIgnoreHiddenJarFiles(ConfigBean.toBoolean(value));
            } else {
                Object[] params = { name, value };
                _logger.log(Level.WARNING,
                            "webcontainer.invalidProperty",
                            params);
            }
        }
    }


    /**
     * Configures the given virtual server with its redirect properties.
     *
     * @param vs The virtual server to configure
     * @param vsBean 
     */
    private void configureRedirect(
        VirtualServer vs,
        com.sun.enterprise.config.serverbeans.VirtualServer vsBean) {

        if (vsBean == null) {
            return;
        }

        ElementProperty[] props = vsBean.getElementProperty();
        if (props == null) {
            return;
        }

        for (int i=0; i<props.length; i++) {

            String propName = props[i].getName();
            String propValue = props[i].getValue();
            if (propName == null || propValue == null) {
                _logger.log(Level.WARNING,
                            "webcontainer.nullVirtualServerProperty",
                            vs.getID());
            }

            if (!propName.startsWith("redirect_")) {
                continue;
            }

            /* 
             * Validate the prop value
             */
            String from = null;
            String url = null;
            String urlPrefix = null;
            String escape = null;

            String[] redirectParams = propValue.split(" ");
            for (int j=0; j<redirectParams.length; j++) {

                if (redirectParams[j].startsWith("from=")) {
                    if (from != null) {
                        _logger.log(Level.WARNING,
                                    "webcontainer.redirectMultipleElement",
                                    new Object[] { propValue, "from" });
                    }
                    from = redirectParams[j].substring("from=".length());
                }

                if (redirectParams[j].startsWith("url=")) {
                    if (url != null) {
                        _logger.log(Level.WARNING,
                                    "webcontainer.redirectMultipleElement",
                                    new Object[] { propValue, "url" });
                    }
                    url = redirectParams[j].substring("url=".length());
                }

                if (redirectParams[j].startsWith("url-prefix=")) {
                    if (urlPrefix != null) {
                        _logger.log(Level.WARNING,
                                    "webcontainer.redirectMultipleElement",
                                    new Object[] { propValue, "url-prefix" });
                    }
                    urlPrefix = redirectParams[j].substring(
                                                    "url-prefix=".length());
                }

                if (redirectParams[j].startsWith("escape=")) {
                    if (escape != null) {
                        _logger.log(Level.WARNING,
                                    "webcontainer.redirectMultipleElement",
                                    new Object[] { propValue, "escape" });
                    }
                    escape = redirectParams[j].substring("escape=".length());
                }
            }

            if (from == null || from.length() == 0) {
                _logger.log(Level.WARNING,
                            "webcontainer.redirectMissingFrom",
                            propValue);
            }

            // Either url or url-prefix (but not both!) must be present
            if ((url == null || url.length() == 0)
                    && (urlPrefix == null || urlPrefix.length() == 0)) {
                _logger.log(Level.WARNING,
                            "webcontainer.redirectMissingUrlOrUrlPrefix",
                            propValue);
            }
            if (url != null && url.length() > 0
                    && urlPrefix != null && urlPrefix.length() > 0) {
                _logger.log(Level.WARNING,
                            "webcontainer.redirectBothUrlAndUrlPrefix",
                            propValue);
            }

            boolean escapeURI = true;
            if (escape != null) {
                if ("yes".equalsIgnoreCase(escape)) {
                    escapeURI = true;
                } else if ("no".equalsIgnoreCase(escape)) {
                    escapeURI = false;
                } else {
                    _logger.log(Level.WARNING,
                                "webcontainer.redirectInvalidEscape",
                                propValue);
                }
            }

            vs.addRedirect(from, url, urlPrefix, escapeURI);
        }

    }


    /**
     * Configures the given virtual server with its send-error properties.
     *
     * @param vs The virtual server to be configured
     * @param vsBean The virtual server bean
     */
    private void configureErrorPage(
        VirtualServer vs,
        com.sun.enterprise.config.serverbeans.VirtualServer vsBean) {

        ErrorPage errorPage = null;

        if (vsBean == null) {
            return;
        }

        ElementProperty[] props = vsBean.getElementProperty();
        if (props == null) {
            return;
        }

        for (int i=0; i<props.length; i++) {

            String propName = props[i].getName();
            String propValue = props[i].getValue();
            if (propName == null || propValue == null) {
                _logger.log(Level.WARNING,
                            "webcontainer.nullVirtualServerProperty",
                            vs.getID());
            }

            if (!propName.startsWith("send-error_")) {
                continue;
            }

            /* 
             * Validate the prop value
             */
            String path = null;
            String reason = null;
            String status = null;

            String[] errorParams = propValue.split(" ");
            for (int j=0; j<errorParams.length; j++) {

                if (errorParams[j].startsWith("path=")) {
                    if (path != null) {
                        _logger.log(Level.WARNING,
                                    "webcontainer.sendErrorMultipleElement",
                                    new Object[] { propValue, "path" });
                    }
                    path = errorParams[j].substring("path=".length());
                }

                if (errorParams[j].startsWith("reason=")) {
                    if (reason != null) {
                        _logger.log(Level.WARNING,
                                    "webcontainer.sendErrorMultipleElement",
                                    new Object[] { propValue, "reason" });
                    }
                    reason = errorParams[j].substring("reason=".length());
                }

                if (errorParams[j].startsWith("code=")) {
                    if (status != null) {
                        _logger.log(Level.WARNING,
                                    "webcontainer.sendErrorMultipleElement",
                                    new Object[] { propValue, "code" });
                    }
                    status = errorParams[j].substring("code=".length());
                }
            }

            if (path == null || path.length() == 0) {
                _logger.log(Level.WARNING,
                            "webcontainer.sendErrorMissingPath",
                            propValue);
            }

            errorPage = new ErrorPage();
            errorPage.setLocation(path);
            errorPage.setErrorCode(status);
            errorPage.setReason(reason);

            vs.addErrorPage(errorPage);
        }

    }


    /*
     * Gets the instance classpath, which is composed of the pathnames of
     * domain_root/lib/classes and domain_root/lib/[*.jar|*.zip] (in this
     * order), separated by the path-separator character.
     *
     * @param instanceEnv The instance whose classpath is to be determined
     *
     * @return The instance classpath
     */
    private String getInstanceClassPath(InstanceEnvironment instanceEnv) {

        if (instanceEnv == null) {
            return null;
        }

        StringBuffer sb = new StringBuffer();

        File libDir = new File(instanceEnv.getLibPath());
        String libDirPath = libDir.getAbsolutePath();

        // Append domain_root/lib/classes
        sb.append(libDirPath + File.separator + "classes");
        sb.append(File.pathSeparator);

        // Append domain_root/lib/[*.jar|*.zip]
        String[] files = libDir.list();
        if (files != null) {
            for (int i=0; i<files.length; i++) {
                if (files[i].endsWith(".jar") || files[i].endsWith(".zip")) {
                    sb.append(libDirPath + File.separator + files[i]);
                    sb.append(File.pathSeparator);
                }
            }
        }

        return sb.toString();
    }


    /*
     * Gets the instance classpath, which is composed of the pathnames of
     * domain_root/lib/classes and domain_root/lib/[*.jar|*.zip] (in this
     * order), separated by the path-separator character.
     *
     * @return The instance classpath
     */
    String getInstanceClassPath() {
        return this.instanceClassPath;
    }
  
}
         
