/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 * 
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, 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/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package com.sun.enterprise.server;

import com.sun.enterprise.config.serverbeans.ExtensionModule;
import com.sun.enterprise.config.serverbeans.ElementProperty;
import com.sun.enterprise.config.serverbeans.J2eeApplication;
import com.sun.enterprise.config.serverbeans.ServerBeansFactory;
import com.sun.enterprise.config.ConfigContext;
import com.sun.enterprise.config.ConfigException;
import com.sun.enterprise.Switch;
import com.sun.enterprise.deployment.WebBundleDescriptor;
import com.sun.enterprise.deployment.Application;
import com.sun.enterprise.deployment.interfaces.pluggable.ArchiveDescriptor;
import com.sun.enterprise.instance.ExtensionModuleConfigManager;
import com.sun.enterprise.security.acl.RoleMapper;
import com.sun.enterprise.util.io.FileUtils;
import com.sun.enterprise.web.WebModuleConfig;
import com.sun.enterprise.web.WebContainer;
import com.sun.logging.LogDomains;

import java.lang.Exception;
import java.lang.Throwable;
import java.io.File;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.enterprise.deploy.shared.ModuleType;
import javax.naming.NamingException;

/**
 * This class is a wrapper class that abstracts the loading/unloading functionality
 * of the WebContainer, by providing utility methods that would call into the
 * WebContainer.java . This class can be used by any Loader that would be used 
 * to deploy an ExtensionModule on the Web Container.
 * @author prasad.subramanian@sun.com
 */
public class WebArchiveLoader extends AppArchiveLoader {
    
    private ExtensionModuleConfigManager configManager;
    private ModuleType moduleType;
    
    static Logger _logger = LogDomains.getLogger(LogDomains.LOADER_LOGGER);
    
    /**
     * Creates a new instance of WebArchiveLoader
     */
    public WebArchiveLoader() {
    }
    
    /**
     * Utility method to get an ExtensionModule config bean from the 
     * ExtensionModuleConfigManager using the moduleID as the key.
     * @param modID the moduleID
     * @return ExtensionModule
     */    
    public ExtensionModule getExtensionModule(String modId) {
        try {
            return configManager.getExtensionModule(modId);
        } catch (ConfigException ce) {
            //ce.printStrackTrace();
            return null;
        }
        
    }

    /**
     * Utility method to construct a WebModuleConfig object
     * @param em ExtensionModule config bean
     * @param wbd the WebBundleDescriptor object for this module
     * @return WebModuleConfig 
     */
    public WebModuleConfig getModuleConfig(ExtensionModule em,
                                                      WebBundleDescriptor wbd) {
        
        WebModuleConfig wmInfo = new WebModuleConfig();       
        wmInfo.setBean(em);
        String wmID = em.getName();
        try {
            wmInfo.setDescriptor(wbd);
            String vs = configManager.getVirtualServers(wmID);
            wmInfo.setVirtualServers(vs);
        } catch (Exception ce) {
            wmInfo = null;
        }
        return wmInfo;        
    }
    
    /**
     * Wrapper method to load a Extended WebModule
     * @param wmInfo WebModuleConfig object for this module
     * @return boolean success/failure
     * @throws Exception 
     */    
    public boolean loadWebModule(WebModuleConfig wmInfo) throws Exception {

        List<Throwable> throwables =
            getWebContainer().loadWebModule(wmInfo, "null");
        if (throwables != null && !throwables.isEmpty()) {
            //we don't have the ability to return all errors.  so return the
            //first one, enough to signal the problem.
            String msg = throwables.get(0).getMessage();
           Exception ex = new Exception(msg);
            ex.initCause(throwables.get(0));
            throw ex;
        } else {
            return true;
        }
   
    }
    
    /**
     * Wrapper method to load a Extended WebModule
     * @param em ExtensionModule config bean for this module
     * @param wbd WebBundleDescriptor for this module
     * @return boolean success/failure
     * @throws Exception 
     */    
    public boolean unloadWebModule(ExtensionModule em, WebBundleDescriptor wbd)
                                                              throws Exception {

        String appName = null;
        try {

            Application app = configManager.getExtensionDescriptor(
                     em.getName(), em.getLocation(), true);
            RoleMapper.removeRoleMapper(app.getRoleMapper().getName());
            appName = app.getRegistrationName();
        } catch (Exception ce) {
            throw new Exception(ce);
        }

        String contextRoot = null;
        ElementProperty ep = em.getElementPropertyByName("contextRoot");
        if(ep != null ){
            contextRoot = ep.getValue();
        } else {
            contextRoot = em.getName();
        }
              
        String virtualServers = null;
        try {
            virtualServers = configManager.getVirtualServers(em.getName());
        } catch(Exception ce) {
                 throw new Exception("Exception getting virtual servers by app name "
                    + appName, ce);
           
        }

        try {
            getWebContainer().unloadWebModule(contextRoot, appName,
                                         virtualServers, wbd);
            // XXX : Fix me 
            return true;
        } catch (Exception ex) {
            _logger.log(Level.WARNING,"Exception thrown in the unload" + ex.toString());
            return false;
        } finally {
            try {
              Switch.getSwitch().getNamingManager().unbindObjects(wbd);
            } catch (javax.naming.NamingException nameEx) {
                throw new Exception("[WebExtensionLoader] "
                + " Exception during namingManager.unbindObject",
                nameEx);
            }
        }
         
    }
    
    /**
     * Wrapper method to enable a Extended WebModule
     * @param wmInfo WebModuleConfig object for this module
     * @return boolean success/failure
     * @throws Exception 
     */
    public boolean enableWebModule(WebModuleConfig wmInfo) throws Exception {
        getWebContainer().enableWebModule(wmInfo, "null");
        // FIX ME : Need to refactor the WebContainer to return status.
        return true;
    }
    
    /**
     * Wrapper method to disable a Extended WebModule
     * @param em ExtensionModule bean for this module
     * @return boolean success/failure
     * @throws Exception 
     */    
    public boolean disableWebModule(ExtensionModule em) throws Exception {
    
        String appName = null;
        try {
            Application app = configManager.getExtensionDescriptor(
                     em.getName(), em.getLocation(), true);
            RoleMapper.removeRoleMapper(app.getRoleMapper().getName());
            appName = app.getRegistrationName();
        } catch (Exception ce) {
            throw new Exception(ce);
        }
        
        String contextRoot = null;
        ElementProperty ep = em.getElementPropertyByName("contextRoot");
        if(ep != null ){
            contextRoot = ep.getValue();
        } else {
            contextRoot = em.getName();
        }
        
        String virtualServers = null;
        try {
            virtualServers = configManager.getVirtualServers(em.getName());
        } catch(Exception ce) {
                 throw new Exception("Exception getting virtual servers by app name "
                    + appName, ce);
           
        }    
        
        return getWebContainer().disableWebModule(contextRoot, 
                                                     appName, virtualServers);
    }
    
    /** 
     * This method asbtracts the functionality for loading an Extension Module
     * in the WebContainer
     * @param descriptor the descriptor for the module
     * @return boolean true if success
     * @throws Exception
     */
    public boolean loadJ2eeApplicationModule(ArchiveDescriptor descriptor) 
                                                             throws Exception {
        
        com.sun.enterprise.config.serverbeans.ExtensionModule em = null;
        WebModuleConfig wmInfo = null;   
        Application appDesc = null;

        J2eeApplication j2eeAppBean = getJ2eeAppBean(); 
        ConfigContext eventConfigContext = j2eeAppBean.getConfigContext();
        String id = j2eeAppBean.getName();
        String location = j2eeAppBean.getLocation();
        String resourceType = j2eeAppBean.getObjectType();  
        
        //TODO : Check if enabled and take action
        
        // get the application descriptor
        ApplicationRegistry registry = ApplicationRegistry.getInstance();
        ClassLoader appLoader = registry.getClassLoaderForApplication(id);
        if (appLoader != null) {
            appDesc = registry.getApplication(appLoader); 
        }
        // If the Application object is null then we should not load.
        if(appDesc == null ) {
            // log message
            return false;
        }
        String j2eeApplication = appDesc.getRegistrationName();
        
        // get the Bundle Descriptor
        WebBundleDescriptor wbd = (WebBundleDescriptor)descriptor;
        String moduleName = wbd.getModuleDescriptor().getArchiveUri();
                            
        StringBuffer dir = new StringBuffer(location);
        dir.append(File.separator);
        dir.append(FileUtils.makeFriendlyFilename(moduleName));
        if (!(new File(dir.toString()).exists())) {
            dir = new StringBuffer(location).append(
                            File.separator).append(moduleName);
        }

        // set the values in the ExtensionModule config bean
        em = new com.sun.enterprise.config.serverbeans.ExtensionModule();
        em.setLocation(dir.toString().trim());
        em.setConfigContext(eventConfigContext);
        em.setName(moduleName);
        ElementProperty isConvergedProperty = new ElementProperty();
        isConvergedProperty.setName("isConverged");
        if(wbd.getContextRoot() != null) {
            isConvergedProperty.setValue("true");
            ElementProperty contextRootProperty = new ElementProperty();
            contextRootProperty.setName("contextRoot");
            contextRootProperty.setValue(wbd.getContextRoot());
            em.addElementProperty(contextRootProperty);
        } else {
            isConvergedProperty.setValue("false");
        }   
        em.addElementProperty(isConvergedProperty);
        em.setEnabled(true);
        em.setObjectType(resourceType);

        // create and populate the WebModuleConfig object
        wmInfo = new WebModuleConfig();
        
        wmInfo.setBean(em);
        wmInfo.setApplicationBean(j2eeAppBean);
        wmInfo.setDescriptor(wbd);
        wmInfo.setParentLoader(appLoader);
        wmInfo.setVirtualServers(getVirtualServers(id, eventConfigContext));
        
        // Call into the WebContainer
        List<Throwable> throwables =
                getWebContainer().loadWebModule(wmInfo, j2eeApplication);
        setJ2eeAppBean(null);
        if (throwables != null && !throwables.isEmpty()) {
            //we don't have the ability to return all errors.  so return the
            //first one, enough to signal the problem.
            String msg = throwables.get(0).getMessage();
            Exception ex = new Exception(msg);
            ex.initCause(throwables.get(0));
            throw ex;
        } else {
            return true;
        }
        
    }
    
    /**
     * Method that process a module and call the WebContainer for unloading 
     * an extension module within an Application
     * @param bd the BundleDescriptor object for the module being unloaded
     * @return boolean true if success
     * @throws Exception
     */
    public boolean unloadJ2eeApplicationModule(ArchiveDescriptor bd) 
                                                             throws Exception {
        String virtualServers = null;
        try {
            virtualServers = getAppsManager().
                        getVirtualServersByAppName(getJ2eeAppBean().getName());
        } catch(ConfigException ce) {
                _logger.log(Level.WARNING, "[WebExtensionLoader] unload " 
                                + getJ2eeAppBean().getName() 
                                + ". error getting virtualServers", ce);
        }   
        
        String appName = ((WebBundleDescriptor)bd).
                                       getApplication().getRegistrationName();
        try {
	   getWebContainer().unloadWebModule(
                         ((WebBundleDescriptor)bd).getContextRoot(), appName,
                                                   virtualServers,
                                                       (WebBundleDescriptor)bd);
        } finally {
	    try {
	        Switch.getSwitch().getNamingManager().unbindObjects(
                                                     ((WebBundleDescriptor)bd));
                setJ2eeAppBean(null);
	    } catch (javax.naming.NamingException nameEx) {
		_logger.log(Level.WARNING, "[WebExtensionLoader] "
			+ " Exception during namingManager.unbindObject",
			nameEx);
	    }
	}
        return true;
    }
    
        /**
     * 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) {

        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;
    }
    
    /**
     * Setter method for ExtensionModuleConfigManager
     * @param  configManager the instance of the ExtensionModuleConfigManager set
     *                       by the ExtensionModuleLoader
     */
    public void setModulesManager(ExtensionModuleConfigManager configManager) {
        this.configManager = configManager;
    }    
    
    protected WebContainer getWebContainer() {
        return WebContainer.getInstance();
    }
    
}
