/*
 * 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.tools.upgrade;

import java.util.*;
import java.io.*;
import java.util.logging.*;

import com.sun.enterprise.tools.upgrade.common.*;
import com.sun.enterprise.tools.upgrade.logging.*;
import com.sun.enterprise.cli.framework.*;
import com.sun.enterprise.util.i18n.StringManager;
import com.sun.enterprise.tools.upgrade.cluster.*;
import com.sun.enterprise.tools.upgrade.miscconfig.ConfigTransfers;
import com.sun.enterprise.tools.upgrade.transform.TransformManager;

public class UpgradeHarness {
    private static Logger logger = LogService.getLogger(LogService.UPGRADE_LOGGER);
    private StringManager stringManager = StringManager.getManager(LogService.UPGRADE_LOGGER);
    private List moduleList;
    private CommonInfoModel commonInfo;
    private ArrayList doNotCopyList;

    public UpgradeHarness() {
        moduleList = new LinkedList();
        // Create a new Array List for 8.xEE to 8.x EE side by side upgrade
        doNotCopyList = new ArrayList();
        doNotCopyList.add("bin");
        doNotCopyList.add("imq");
        doNotCopyList.add("session-store");
        doNotCopyList.add("docroot");
    }
    public void setCommonInfoModel(CommonInfoModel commonInfo){
        this.commonInfo=commonInfo;
    }
    public void startUpgrade(){
        DomainsProcessor dProcessor = new DomainsProcessor(this.commonInfo);
        String srcInstallDir = commonInfo.getSourceInstallDir();

        if(commonInfo.checkSourceInputAsDomainRoot(srcInstallDir)) {
            commonInfo.setSourceDomainRoot(srcInstallDir);
        }			
        // If this is a 8.x EE to EE Upgrade , we want to take a different path
        if(commonInfo.checkUpgradefrom8xeeto9x() ) {
            try {
                loadModules();
                //Added for CR 6480041 - Upgrade flow changed for IP and SBS EE->EE.
                dProcessor.processTargetDomains();
                UpdateProgressManager.getProgressManager().processUpgradeUpdateEvent(30);					
                if(!dProcessor.processClusters()) {
                    dProcessor.processStandAloneInstances();
                }						 
                UpdateProgressManager.getProgressManager().processUpgradeUpdateEvent(50);					

                int totalIterations = this.getTotalIterationsFromDomainMapping();
                int currentIteration =0;
                for(java.util.Iterator dItr = commonInfo.getDomainMapping().keySet().iterator(); dItr.hasNext();){
                    DomainInfo dInfo = (DomainInfo)commonInfo.getDomainMapping().get(dItr.next());
                    commonInfo.setCurrentDomain(dInfo.getDomainName());
                    List instanceList = this.getProcessableInstanceList(dInfo);
                    logger.log(Level.INFO, 
                            stringManager.getString("enterprise.tools.upgrade.currentlyProcessingDomain",
                            dInfo.getDomainName()));
                    for(int instIndex = 0; instIndex < instanceList.size(); instIndex++){
                        currentIteration++;
                        String currentInstanceName = (String)instanceList.get(instIndex);
                        commonInfo.setCurrentSourceInstance(currentInstanceName);
                        logger.log(Level.INFO, 
                                stringManager.getString("enterprise.tools.upgrade.currentlyProcessingInstance",
                                currentInstanceName));
                        if(!invokeModules(currentIteration,totalIterations)) {
                            return;
                        }
                    }
                    if(copyAdminKeyFile(dInfo)) {
                        logger.log(Level.INFO, 
                                stringManager.getString("enterprise.tools.upgrade.copykeyfile"));
                    }
                }						 
                //End - added					 
                logger.log(Level.INFO, 
                    stringManager.getString("enterprise.tools.upgrade.finishedUpgrade"));
                    UpdateProgressManager.getProgressManager().processUpgradeUpdateEvent(100);            
            } catch(HarnessException he) {
                logger.log(Level.SEVERE,stringManager.getString(
                        "enterprise.tools.upgrade.generalException" ,
                        he.getMessage()));
                UpdateProgressManager.getProgressManager().processUpgradeUpdateEvent(-1);
                commonInfo.recover();
                return;                
            }
        } else {
            loadModules();
            System.setProperty("com.sun.aas.configRoot", 
                    commonInfo.getTargetInstallDir() + File.separator + "config");
            try {
                //new DomainsProcessor(this.commonInfo,false);
                dProcessor.processTargetDomains();
                // If domains are created then 50% done
                UpdateProgressManager.getProgressManager().processUpgradeUpdateEvent(30);
                // This if statement is added for AS9.0 uprade scenario form source AS8.x
                /*if(commonInfo.getSourceVersion().equals(UpgradeConstants.VERSION_7X)){
                    if(!commonInfo.getSourceEdition().equals(UpgradeConstants.EDITION_PE)){
                        // This need to be done only for SE and EE.
                        if(commonInfo.getSourceEdition().equals(UpgradeConstants.EDITION_EE)){
                            // Its EE, process clusters.
                            if(dProcessor.processClusters()){
                                this.upgradeEESource();
							    //CR 6418644 - shouldn't return here. other modules to be invoked.
                                //return;
                            }else{
                                // No clinstance files are provided, so just create stand alone instances.
                                dProcessor.processDomainInstances();
                            }                        
                        }else{
                            // Its SE. need to process only instances, no clusters
                            dProcessor.processDomainInstances();
                        }
                    }else{
                        if((commonInfo.getSourceVersion().equals(UpgradeConstants.VERSION_7X)) && 
                                (!commonInfo.getTargetEdition().equals(UpgradeConstants.EDITION_PE))){
                            // In this case AS7.x has server1.  Will crete server1 instance in AS8.1 SE/EE
                            dProcessor.processDomainInstances();
                        }
                    }
                }else{*/
                    // Now its AS8.x to AS 9.0 upgrade.
                    if(commonInfo.getSourceEdition().equals(UpgradeConstants.EDITION_EE)){
                        // Its EE, process clusters.
                        dProcessor.processClusters();                        
                    }                
               // }            
            }catch(HarnessException he) {
                logger.log(Level.SEVERE,
                        stringManager.getString("enterprise.tools.upgrade.generalException",
                        he.getMessage()));
                UpdateProgressManager.getProgressManager().processUpgradeUpdateEvent(-1);
                commonInfo.recover();
                return;
            }
            // If domains are processed then 50% done
            UpdateProgressManager.getProgressManager().processUpgradeUpdateEvent(50);
        
            int totalIterations = this.getTotalIterationsFromDomainMapping();
            int currentIteration =0;
            for(java.util.Iterator dItr = commonInfo.getDomainMapping().keySet().iterator(); dItr.hasNext();){
                DomainInfo dInfo = (DomainInfo)commonInfo.getDomainMapping().get(dItr.next());
                commonInfo.setCurrentDomain(dInfo.getDomainName());
                List instanceList = this.getProcessableInstanceList(dInfo);
                logger.log(Level.INFO, 
                    stringManager.getString("enterprise.tools.upgrade.currentlyProcessingDomain",
                    dInfo.getDomainName()));
                for(int instIndex = 0; instIndex < instanceList.size(); instIndex++){
                    currentIteration++;
                    String currentInstanceName = (String)instanceList.get(instIndex);
                    commonInfo.setCurrentSourceInstance(currentInstanceName);
                    logger.log(Level.INFO, 
                        stringManager.getString("enterprise.tools.upgrade.currentlyProcessingInstance",
                        currentInstanceName));
                    if(!invokeModules(currentIteration,totalIterations)) {
                        return;
                    }
                }
                if(copyAdminKeyFile(dInfo)) {
                    logger.log(Level.INFO, 
                        stringManager.getString("enterprise.tools.upgrade.copykeyfile"));
                }
            }
            logger.log(Level.INFO, stringManager.getString("enterprise.tools.upgrade.finishedUpgrade"));
            //logger.log(Level.CONFIG, Integer.toString(100));
            UpdateProgressManager.getProgressManager().processUpgradeUpdateEvent(100);
        }
	    //Admin credential changes. Added for CR 6454007- deleting the temp password files created
        logger.log(Level.INFO,
            stringManager.getString("enterprise.tools.upgrade.deletingTempPasswordFiles"));
        List passwordFileList = commonInfo.getPasswordFileList();
        for( int k=0; k < passwordFileList.size(); k++) {
            File tempFile = new File((String)passwordFileList.get(k));
            if(tempFile.exists()) {
                tempFile.delete();
            }
        }
        //Admin credential changes - end
    }

    private int getTotalIterationsFromDomainMapping(){
        int totalIt = 1;
        for(java.util.Iterator dItr = commonInfo.getDomainMapping().keySet().iterator(); dItr.hasNext();){
            DomainInfo dInfo = (DomainInfo)commonInfo.getDomainMapping().get(dItr.next());
            List instanceList = this.getProcessableInstanceList(dInfo);
            totalIt += instanceList.size();
        }
        return totalIt;
    }
    private boolean invokeModules(int currentIteration, int totalIterations){
        String moduleName = "Default";
        List successfulModuleList = new ArrayList();
        int moduleSize = moduleList.size();
        int nthModule = 0;
        int progress =0 ;
        for(int i=0; i<moduleSize; i++){            
            nthModule++;
            if(!UpdateProgressManager.getProgressManager().canContinueUpgrade()){
                // If any of the module failed to complete, would have updated this status.
                // In that case should not proceed further.  Should exit out of upgrade.
                // This would be a good place for rollback ..... TBD
                logger.log(Level.SEVERE, stringManager.getString("enterprise.tools.upgrade.didNotfinishUpgrade", commonInfo.getTargetDomainRoot()));
                UpdateProgressManager.getProgressManager().processUpgradeUpdateEvent(-1);
                return false;
            }
            //logger.log(Level.FINE, "processing module :"+moduleName);
            BaseModule baseModule = (BaseModule)this.moduleList.get(i);
            moduleName = baseModule.getName();
            if(baseModule.upgrade(commonInfo))
                successfulModuleList.add(baseModule);
            else { // Rollback all previous successful operations.
                logger.log(Level.INFO,stringManager.getString("enterprise.tools.upgrade.module_upgrade_failed",moduleName));
                UpdateProgressManager.getProgressManager().setContinueUpgrade(false);
                baseModule.recovery(commonInfo);
                for(int k=0; k<successfulModuleList.size(); k++){
                    BaseModule successModule = (BaseModule)successfulModuleList.get(k);
                    logger.log(Level.INFO,stringManager.getString("enterprise.tools.upgrade.module_rollback",successModule.getName(),commonInfo.getCurrentDomain()));
                    successModule.recovery(commonInfo);
                }
            }
            logger.log(Level.INFO, stringManager.getString("enterprise.tools.upgrade.finishedModule")+moduleName);
            //logger.log(Level.CONFIG, Integer.toString((nthModule/size)*100));
            //progress = 50 + (((currentIteration * 50)/totalIterations)  - ((50-(50*nthModule)/moduleSize)));
            int pFirst = (50 * currentIteration) / totalIterations ;
            int pLast = (50/totalIterations) - (50*nthModule)/(moduleSize*totalIterations);
            progress = 50 + pFirst - pLast;
            UpdateProgressManager.getProgressManager().processUpgradeUpdateEvent(progress);
        }
        return true;
    }
    private void upgradeEESource(){        
        // Add 1 for post updateMiscellaneousClusterInfo
        int totalIterations = ClustersInfoManager.getClusterInfoManager().getClusterInfoList().size()+
                                UpgradeUtils.getUpgradeUtils(commonInfo).getStandAloneInstances(commonInfo.getDomainMapping()).size()+1;
        int currentIteration =0;
        // Start with upgrading clustered instances
        for(java.util.Iterator dItr = ClustersInfoManager.getClusterInfoManager().getClusterInfoList().iterator(); dItr.hasNext();){
            ClusterInfo cInfo = (ClusterInfo)dItr.next();
            ClusteredInstance clInstance = cInfo.getMasterInstance();
            if(clInstance == null){
                clInstance = (ClusteredInstance)cInfo.getClusteredInstanceList().get(0);
            }
            commonInfo.setCurrentCluster(cInfo.getClusterName());
            commonInfo.setCurrentDomain(clInstance.getDomain());
            commonInfo.setCurrentSourceInstance(clInstance.getInstanceName());
            currentIteration++;
            logger.log(Level.INFO, stringManager.getString("enterprise.tools.upgrade.currentlyProcessingCLUSTER",cInfo.getClusterName()));
            if(!invokeModules(currentIteration,totalIterations))
                return;            
        }
        commonInfo.setCurrentCluster(null);
        // now upgrade stand alone instances
        for(java.util.Iterator sItr = UpgradeUtils.getUpgradeUtils(commonInfo).getStandAloneInstances(commonInfo.getDomainMapping()).iterator(); sItr.hasNext();){
            Vector instDInfo = (Vector)sItr.next();
            commonInfo.setCurrentDomain(((DomainInfo)instDInfo.elementAt(1)).getDomainName());
            commonInfo.setCurrentSourceInstance((String)instDInfo.elementAt(0));
            currentIteration++;
            logger.log(Level.INFO, stringManager.getString("enterprise.tools.upgrade.currentlyProcessingInstance",(String)instDInfo.elementAt(0)));
            if(!invokeModules(currentIteration,totalIterations))
                return;            
        }
        // Migrate iiop-cluster info saved in clustered info manager.
        this.updateMiscellaneousClusterInfo();
        logger.log(Level.INFO, stringManager.getString("enterprise.tools.upgrade.finishedUpgrade"));
        //logger.log(Level.CONFIG, Integer.toString(100));
        UpdateProgressManager.getProgressManager().processUpgradeUpdateEvent(100);
    }
    private void updateMiscellaneousClusterInfo(){
        // Get IIOPClusters from clusterinfo manager and process them
        Hashtable iiopMapping = ClustersInfoManager.getClusterInfoManager().getIIOPClustersMapping();
        if(iiopMapping != null){
            for(Iterator it = iiopMapping.keySet().iterator(); it.hasNext();){
                String domainName = (String)it.next();
                List iCls = (List)iiopMapping.get(domainName);
                String domainXMLfile = this.commonInfo.getDestinationDomainPath(domainName)+File.separator+"config"+File.separator+"domain.xml";
                for(int i=0; i<iCls.size(); i++){
                    IIOPCluster iiopCluster = (IIOPCluster)iCls.get(i);
                    UpgradeUtils.getUpgradeUtils(this.commonInfo).updateDomainXMLWithIIOPCluster(this.commonInfo, domainXMLfile, iiopCluster);                    
                }
            }
        }
        // Get PersistenceStore info from cluster info manager and process them
        Hashtable persistenceStoreMapping = ClustersInfoManager.getClusterInfoManager().getPersistenceStorePropertiesMapping();
        if(persistenceStoreMapping != null){
            for(Iterator pit = persistenceStoreMapping.keySet().iterator(); pit.hasNext();){
                String domainName = (String)pit.next();
                java.util.Properties props = (java.util.Properties)persistenceStoreMapping.get(domainName);
                String domainXMLfile = this.commonInfo.getDestinationDomainPath(domainName)+File.separator+"domain.xml";
                UpgradeUtils.getUpgradeUtils(this.commonInfo).updateDomainXMLWithPersistenceStoreProps(props);
            }
        }
    }
    private List getProcessableInstanceList(DomainInfo dInfo){
        List instanceList = new ArrayList();
        List domainInstanceList = dInfo.getInstanceNames();
        if(commonInfo.getSourceVersionAndEdition().equals(UpgradeConstants.VERSION_AS80_PE)||
           commonInfo.getSourceVersionAndEdition().equals(UpgradeConstants.VERSION_AS81_PE) ||
           commonInfo.getSourceVersionAndEdition().equals(UpgradeConstants.VERSION_AS80_SE) ||
           commonInfo.getSourceVersionAndEdition().equals(UpgradeConstants.VERSION_AS81_EE) ||
           commonInfo.getSourceVersionAndEdition().equals(UpgradeConstants.VERSION_AS82_PE) ||
           commonInfo.getSourceVersionAndEdition().equals(UpgradeConstants.VERSION_AS90_PE) ||
           //Added for CR 6480041
           commonInfo.getSourceVersion().equals(UpgradeConstants.VERSION_91)){
           //End - added			
            instanceList.add(new String(""));
            return instanceList;
        }
        for(int i=0; i<domainInstanceList.size(); i++){
            String instanceName = (String)domainInstanceList.get(i);
            if(commonInfo.getSourceVersion().equals(UpgradeConstants.VERSION_7X)){
                // For 7x remove admin server from the list
                if(instanceName.equals("admin-server"))
                    continue;
                instanceList.add(instanceName);
                if(commonInfo.getSourceEdition().equals(UpgradeConstants.EDITION_PE)){
                    // IF PE only return the first element  fromt he list
                    if (domainInstanceList.size() > 2) {
                        logger.log(Level.INFO,stringManager.getString("enterprise.tools.upgrade.more_thanone_instance",instanceName));
                    }
                    break;
                }
            }
        }
        if(instanceList.isEmpty())
            logger.log(Level.WARNING, stringManager.getString("enterprise.tools.upgrade.no_server_instance", dInfo.getDomainName()));
        return instanceList;
    }
    private void loadModules(){
        List list = CommonProperties.getRegisteredModules(commonInfo);
        int size = list.size();
        for(int i=0; i<size; i++){
            String moduleClassName = (String)list.get(i);
            try{
                Class cls = Class.forName(moduleClassName);
                BaseModule baseModule = (BaseModule)cls.newInstance();
                moduleList.add(baseModule);
            }catch(ClassNotFoundException e){
                logger.log(Level.SEVERE,stringManager.getString("enterprise.tools.upgrade.load_instantiate_error", e.getMessage()),e);
            }catch(InstantiationException e){
                logger.log(Level.SEVERE,stringManager.getString("enterprise.tools.upgrade.load_instantiate_error", e.getMessage()),e);
            }catch(IllegalAccessException e){
                logger.log(Level.SEVERE,stringManager.getString("enterprise.tools.upgrade.load_instantiate_error", e.getMessage()),e);
            }
        }
    }

    public void copyDomainsFromBackup(String domainName) {
        DomainInfo domainInfo = (DomainInfo)commonInfo.getDomainMapping().get(domainName);
        String domainPath = domainInfo.getDomainPath();
        // FIX ME : Need to find a better way to copy over the directory.I hate to do this.
        try {

            UpgradeUtils.getUpgradeUtils(commonInfo).copyDirectory(new File(domainPath),
                new File(commonInfo.getTargetDomainRoot()+File.separator+domainName));
        } catch(IOException ioe) {
            logger.log(Level.WARNING, stringManager.getString("IOException thrown",
                    domainPath));
        }
        // now change permissions of startserv and stopserv
        if(System.getProperty("os.name").equals("Unix") ||
              System.getProperty("os.name").equals("Linux")  ) {
            setExecutePermissions(commonInfo.getTargetDomainRoot()+
                      File.separator+domainName);
        } 
       
        
    }
    
    public void setExecutePermissions(String domainPath) {
        String binDir = domainPath + File.separator + "bin";
        String generatedDir = domainPath + File.separator + "generated" +
                File.separator + "tmp";
        logger.log(Level.INFO,"bin dir= "+binDir);
        try {
            final String[] fileList = new File(binDir).list();
            for (int i = 0 ; i < fileList.length ; i ++) {
                logger.log(Level.INFO,"File List = "+binDir+ File.separator + fileList[i]);
                Runtime.getRuntime().exec("/bin/chmod a+x " +
                        binDir + File.separator + fileList[i]);
            }
            // this tmp directory needs to be rwx------
            Runtime.getRuntime().exec("/bin/chmod 700 " +
                        generatedDir );
         } catch (Exception e) {
             logger.log(Level.WARNING, 
                    stringManager.getString("domainsProcessor.IOException",
                    domainPath));               
         }
    }
    
    /** This method would copy the admin-keyfile which is the default store of the
     * admin credentials. This method is effective only for 8.x PE to 8.2 EE upgrades
     * 
     * @param dInfo the DomainInfo object for the domain being processed
     */ 
    
    public boolean copyAdminKeyFile(DomainInfo dInfo) {
        // do this only for 8.xPE/EE->9.1 EE and 9.0PE->9.1EE
        if(!(commonInfo.checkUpgradefrom8xpeto9x() || 
                commonInfo.checkUpgradefrom9xpeto9x() ||
                commonInfo.checkUpgradefrom8xeeto9x())) {
            return false;
        }			
        try {
            String sourceKeyFilePath = 
                    dInfo.getDomainPath()+File.separator+"config";
            String targetKeyFilePath = 
                    commonInfo.getTargetDomainRoot() +
                                   File.separator +
                                   dInfo.getDomainName()+
                                   File.separator+
                                   "config";
            String sourceKeyFile = sourceKeyFilePath + File.separator + "admin-keyfile";
            String targetKeyFile = targetKeyFilePath + File.separator + "admin-keyfile";
            if(!(new File(sourceKeyFile).exists())) 
                return false;
            // now copy the file
            UpgradeUtils.getUpgradeUtils(commonInfo).copyFile(sourceKeyFile,
                                                            targetKeyFile);
            
        } catch (IOException ioe ) {
            logger.log(Level.WARNING, 
                    stringManager.getString("domainsProcessor.IOException",
                     ioe.getMessage()));  
             return false;
        }
        return true;
     }
    
    public void copyDomain(String sourceDomain, String targetDomainRoot ) {
        // copy selected directories
        File srcDomain = new File(sourceDomain);
        // target domain is the target domain root + domain name
        File targetDomain = new File(targetDomainRoot,
                             (String)commonInfo.getDomainList().get(0));
        String[] srcDomainListing = srcDomain.list();
        for( int i=0; i< srcDomainListing.length; i++ ){
            // if this directory should be copied
            //Fix for 6444308
            if(!doNotCopyList.contains(srcDomainListing[i])) {
                File srcDomainSubDirectory = new File(sourceDomain, srcDomainListing[i]);
                File targetDomainSubDirectory = new File(targetDomain, srcDomainListing[i]);
                try {
                    UpgradeUtils.copyDirectory(srcDomainSubDirectory,
                       targetDomainSubDirectory);
                //Fix for 6444308
                } catch (IOException ioe) {
                    logger.log(Level.WARNING, 
                        stringManager.getString("domainsProcessor.IOException",
                            ioe.getMessage()));                     
                }
            } else {
                continue;
            }
        }
        //Fix for 6444308
        // now change permissions of startserv and stopserv
        if(System.getProperty("os.name").equals("Unix") ||
                System.getProperty("os.name").equals("Linux")  ||
                //Fix for CR 6463376
                System.getProperty("os.name").equals("SunOS")) {
            String domainName = (String)commonInfo.getDomainList().get(0);
            setExecutePermissions(commonInfo.getTargetDomainRoot()+
                    File.separator+domainName);
        }
    }
}
