/*
 * 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.
 */

/*
 * EEWebContainerAdminEventProcessor.java
 *
 * Created on September 26, 2003, 4:51 PM
 */

package com.sun.enterprise.web;

import java.util.Hashtable;

import java.util.logging.Logger;
import java.util.logging.Level;
import java.util.ResourceBundle;

import com.sun.logging.LogDomains;

import com.sun.enterprise.admin.event.ApplicationDeployEvent;
import com.sun.enterprise.admin.event.ModuleDeployEvent;

/**
 *
 * @author  lwhite
 */
public class EEWebContainerAdminEventProcessor implements WebContainerAdminEventProcessor {
    
    /**
     * 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 embedded Catalina object.
     */
    protected EmbeddedWebContainer _embedded = null; 
    
    /**
     * Hashtable to keep track of deploy / undeploy events timing
     * HERCULES:add
     */    
    private static Hashtable _deployHistory = new Hashtable();
    
    /** Creates a new instance of EEWebContainerAdminEventProcessor */
    public EEWebContainerAdminEventProcessor() {
        if (_logger == null) {
            _logger = LogDomains.getLogger(LogDomains.WEB_LOGGER);
            _rb = _logger.getResourceBundle();
        }        
    }    
    
    /** Creates a new instance of EEWebContainerAdminEventProcessor */
    public EEWebContainerAdminEventProcessor(EmbeddedWebContainer embedded) {
        _embedded = embedded;
        if (_logger == null) {
            _logger = LogDomains.getLogger(LogDomains.WEB_LOGGER);
            _rb = _logger.getResourceBundle();
        }        
    }
    
    public void init(EmbeddedWebContainer embedded) {
        _embedded = embedded;
    }    
    
    public void applicationDeployed(ApplicationDeployEvent deployEvent) {
        String deployedAppName = deployEvent.getApplicationName();
        _logger.finest("applicationDeployed:" + deployedAppName);
        String key = "App_" + deployedAppName;
        
        boolean deployedRecently = checkDeployHistoryEntry(key, "deployed");
        setDeployHistoryEntry(key, "deployed", System.currentTimeMillis());
        if (deployedRecently) {
            _logger.finest("Returning from MIDDLE of applicationDeployed");
            return;
        }
        
        _logger.finest("about to call SessionPurgeUtil for" + deployedAppName);
        deployedAppName = "/" + deployedAppName;
        /*
        ConnectionShutdownUtil shutdownUtil = new ConnectionShutdownUtil(_instances);
        System.out.println("IN WebContainer>>applicationDeployed: " 
            + deployedAppName + "about to close all connections");
        //shutdownUtil.runCloseAllConnections();
         */
        System.gc();
        //use next line later after container deploy bug is fixed
        //purgeUtil.closeCachedConnectionForApp(deployedAppName);                
        _logger.finest("Returning from END of applicationDeployed");        
    }
    
    public void applicationDisabled(ApplicationDeployEvent deployEvent) {
    }
    
    public void applicationEnabled(ApplicationDeployEvent deployEvent) {
    }
    
    public void applicationRedeployed(ApplicationDeployEvent deployEvent) {
        String deployedAppName = deployEvent.getApplicationName();
        _logger.finest("applicationRedeployed:" + deployedAppName);
        
        String key = "App_" + deployedAppName;
        boolean redeployedRecently = checkDeployHistoryEntry(key, "redeployed");
        setDeployHistoryEntry(key, "redeployed", System.currentTimeMillis());
        if (redeployedRecently) {
            _logger.finest("Returning from MIDDLE of applicationRedeployed");
            return;
        }
                
        SessionPurgeUtil purgeUtil = new SessionPurgeUtil(_embedded);
        _logger.finest("about to call SessionPurgeUtil for" + deployedAppName);
        deployedAppName = "/" + deployedAppName;
        purgeUtil.purgeSessionsForApp(deployedAppName);
        /*
        ConnectionShutdownUtil shutdownUtil = new ConnectionShutdownUtil(_instances);
        System.out.println("IN WebContainer>>applicationRedeployed: " 
            + deployedAppName + "about to close all connections");        
        //shutdownUtil.runCloseAllConnections();
         */
        System.gc();
        //use next line later after container deploy bug is fixed
        //purgeUtil.closeCachedConnectionForApp(deployedAppName);                    
        _logger.finest("Returning from END of applicationRedeployed");        
    }
    
    public void applicationUndeployed(ApplicationDeployEvent deployEvent) {
        String deployedAppName = deployEvent.getApplicationName();
        _logger.finest("applicationUndeployed:" + deployedAppName);
        
        String key = "App_" + deployedAppName;
        boolean undeployedRecently = checkDeployHistoryEntry(key, "undeployed");
        setDeployHistoryEntry(key, "undeployed", System.currentTimeMillis());
        if (undeployedRecently) {
            _logger.finest("Returning from MIDDLE of applicationUndeployed");
            return;
        }
        
        SessionPurgeUtil purgeUtil = new SessionPurgeUtil(_embedded);
        _logger.finest("about to call SessionPurgeUtil for" + deployedAppName);
        deployedAppName = "/" + deployedAppName;
        purgeUtil.purgeSessionsForApp(deployedAppName);
        /*
        ConnectionShutdownUtil shutdownUtil = new ConnectionShutdownUtil(_instances);
        System.out.println("IN WebContainer>>applicationUnDeployed: " 
            + deployedAppName + "about to close all connections");        
        //shutdownUtil.runCloseAllConnections();
         */
        System.gc();
        //use next line later after container deploy bug is fixed
        //purgeUtil.closeCachedConnectionForApp(deployedAppName);              
    }
    
    public void moduleDeployed(ModuleDeployEvent deployEvent) {
        String deployedModuleName = deployEvent.getModuleName();
        _logger.finest("moduleDeployed:" + deployedModuleName);

        String key = "Mod_" + deployedModuleName;
        boolean deployedRecently = checkDeployHistoryEntry(key, "deployed");
        setDeployHistoryEntry(key, "deployed", System.currentTimeMillis());
        if (deployedRecently) {
            _logger.finest("Returning from MIDDLE of moduleDeployed");	
            return;
        }
                
        ConnectionShutdownUtil shutdownUtil = new ConnectionShutdownUtil(_embedded);
        shutdownUtil.runCloseAllConnections();
        System.gc();
        //use next line later after container deploy bug is fixed
        //purgeUtil.closeCachedConnectionForApp(deployedAppName);        
        _logger.finest("Returning from END of moduleDeployed");        
    }
    
    public void moduleDisabled(ModuleDeployEvent deployEvent) {
    }
    
    public void moduleEnabled(ModuleDeployEvent deployEvent) {
    }
    
    public void moduleRedeployed(ModuleDeployEvent deployEvent) {
        String deployedModuleName = deployEvent.getModuleName();
        _logger.finest("moduleRedeployed:" + deployedModuleName);

        String key = "Mod_" + deployedModuleName;
        boolean redeployedRecently = checkDeployHistoryEntry(key, "redeployed");
        setDeployHistoryEntry(key, "redeployed", System.currentTimeMillis());
        if (redeployedRecently) {
            _logger.finest("Returning from MIDDLE of moduleRedeployed");
            return;
        }
                
        ConnectionShutdownUtil shutdownUtil = new ConnectionShutdownUtil(_embedded);
        shutdownUtil.runCloseAllConnections();
        System.gc();
        //use next line later after container deploy bug is fixed
        //purgeUtil.closeCachedConnectionForApp(deployedAppName);         
        _logger.finest("Returning from END of moduleRedeployed");        
    }
    
    public void moduleUndeployed(ModuleDeployEvent deployEvent) {
        String deployedModuleName = deployEvent.getModuleName();
        _logger.finest("moduleUndeployed:" + deployedModuleName);

        String key = "Mod_" + deployedModuleName;
        boolean undeployedRecently = checkDeployHistoryEntry(key, "undeployed");
        setDeployHistoryEntry(key, "undeployed", System.currentTimeMillis());
        if (undeployedRecently) {
            _logger.finest("Returning from MIDDLE of moduleUndeployed");
            return;
        }

        ConnectionShutdownUtil shutdownUtil = new ConnectionShutdownUtil(_embedded);
        System.out.println("IN WebContainer>>moduleUndeployed: " 
            + deployedModuleName + "about to close all connections");        
        shutdownUtil.runCloseAllConnections();
        System.gc();
        //use next line later after container deploy bug is fixed
        //purgeUtil.closeCachedConnectionForApp(deployedAppName);         
        _logger.finest("Returning from END of moduleUndeployed");        
    }
    
    /**
     * set a deploy history entry
     * @param key identifies an application or module
     * @param value may have one of
     *                  the following values : 'deployed', 'undeployed', 'redeployed'.
     * @param lat the latency
     * HERCULES:add
     */
    private void setDeployHistoryEntry(String key, String value, long lat) {
        DeployHistoryEntry entry = (DeployHistoryEntry) _deployHistory.get(key);		if (entry == null)
            _deployHistory.put(key, new DeployHistoryEntry(value, lat));
        else {
            entry.value = value;
            entry.lat = lat;
        }
    } 
    
    /**
     * This method returns true if the application/module is attempted to be  
     * undeployed even though it was already undeployed, or is attempted to be 
     * (re)deployed when it was (re)deployed within the last 1 minute (60000 ms)
     * @param key identifies an application or module
     * @param deployStatus may have one of
     *                  the following values : 'deployed', 'undeployed', 'redeployed'.
     * HERCULES:add
     */    
    private boolean checkDeployHistoryEntry(String key, String deployStatus) {
        DeployHistoryEntry entry = (DeployHistoryEntry) _deployHistory.get(key);
        if (entry == null) {
            return false;
        }
        String val = entry.value;
        if (val.equals(deployStatus)) {
            /*if (deployStatus.equals("undeployed")) {
                    return true; // no need to check timestamp
            }*/
            if (( System.currentTimeMillis() - entry.lat ) < 60000) {
                    return true;
            }
        }
        return false;
    }    
    
}

/**
 * class used by WebContainer to properly handle timing of admin events
 * HERCULES:add
 */
class DeployHistoryEntry {
    String value;
    long lat;

    DeployHistoryEntry(String value, long lat) {
        this.value = value;
        this.lat = lat;
    }
}
