package com.eactive.eai.adapter.wca.cfg;

import com.eactive.eai.adapter.AdapterPropManager;
import com.eactive.eai.adapter.wca.WCASession;
import com.eactive.eai.adapter.wca.WCASessionPool;
import com.eactive.eai.adapter.wca.exception.WCAException;
import com.eactive.eai.adapter.wca.global.WCADefine;
import com.eactive.eai.adapter.wca.jdbc.JDBCException;
import com.eactive.eai.adapter.wca.jdbc.JDBCFactory;
import com.eactive.eai.adapter.wca.util.MyUtil;
import com.eactive.eai.common.server.EAIServerManager;
import com.eactive.eai.common.util.Logger;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;


/** 
* 1. 기능 : EAI 어댑터 그룹 이름에 해당하는 Adapter
* 2. 처리 개요 :
* 3. 주의사항 
*
* @author  :  
* @version : v 1.0.0
* @see     : 
* @since   : 
*/
public class SNAAdapter 
{
    /**
     * SNAAdapter critical한 정보 로깅을 위한 logger 
     */
    Logger adapter_logger = Logger.getLogger(WCADefine.ADAPTER_LOGGER_NAME);

    /**
     * LU 그룹 세션 풀 리스트
     */
    private LinkedList 	listWCALocalSessonPool      = null;
    
    /**
     * 백업 LU 그룹 세션 풀 리스트
     */
    private LinkedList 	listWCABackupSessonPool     = null;
    
    /**
    * 세션풀 get index (round robin 방식으로 세션풀 할당하기 위함)
    */
    private int			nLocalSessionPoolIdx        = 0;
    
    /**
     * 어댑터 그룹 이름 
     */
    public String 		adapterGroupName            = null;
    
    /**
     * 어댑터 이름
     */
    public String 		adapterName                 = null;
    
    int					lugrpCount					= 0;		// 어댑터에 속한 노드별 LU 그룹 갯수
    
    SNAAdapterManager smanager = SNAAdapterManager.getInstance();
   
    /** 
     * 어댑터 그룹 이름, LU 그룹 세션 정보
     * 키 : 어댑터 그룹 이름
     * 값 : lugrpNodeMap (HashMap) 오브젝트
     * 
     * lugrpNodeMap : 
     * 키 : 소프트링크 노드 이름
     * 값 : lugrpGroupMap (HashMap) 오브젝트
     *      
     * lugrpGroupMap : 
     * 키 : LU 그룹 이름
     * 값 : SessionGroupDataEx 클래스 오브젝트(LU 그룹 세션 정보)
     */    
    private HashMap		lugrpNodeMap				= null;
    
    /**
     * threadNodeMap
     * 키 : Softlink Node 이름
     * 값 : threadGroupMap(LU 그룹별 쓰레드 그룹 HashMap 오브젝트)
     * 
     * threadGroupMap
     * 키 : LU 그룹 이름
     * 값 : ThreadGroup(쓰레드 그룹 오브젝트)
     * 
     * threadGroupName  : "어댑터 그룹" + "-" + "노드 이름" + "-" + "LU 그룹 코드"
     * threadName 		: "어댑터 그룹" + "-" + "노드 이름" + "-" + "LU 그룹 코드" + "세션 번호" + [":BACKUP"]
     */    
    private HashMap 	threadNodeMap				= null;
        
    /** 
     * 1. 기능 : Constructor
     * 2. 처리 개요 : 
     * 3. 주의사항 
     * 
     * @param adapterGrpName 어댑터 그룹 이름
     * @param adapterName    어댑터 이름  
     **/
    public SNAAdapter(String adapterGroupName, String adapterName) {
        this.adapterGroupName  	= adapterGroupName;
        this.adapterName		= adapterName;
    }
    
    /** 
     * 1. 기능 : SNA Adapter에 해당하는 LU 그룹 정보 정보를 저장하기 위한 오브젝트 생성 및 LU 그룹에 대한 정보 Load 
     * 2. 처리개요 :
     * 3. 주의사항 :
     * 
     * @param sAdapterGrpName 어댑터 그룹 이름
     * 
     * @return LU 노드 정보 맵의 크기 
     **/    
    public int initAdapter() {
    	String		sLogStr = "[" + adapterGroupName + "]";
    	
        listWCALocalSessonPool  	= new LinkedList();		// 정상 세션플 리스트
        listWCABackupSessonPool 	= new LinkedList();		// 백업 세션풀 리스트
        
        threadNodeMap				= new HashMap();		// 쓰레드 노드 맵
        
        adapter_logger.warn("SNAAdapter:initAdapter> " + sLogStr + " Try To Load LU Group Info");
        
        // LU 그룹 정보 맵을 로드
    	lugrpNodeMap = loadAdapterInfo(adapterGroupName, adapterName);
    	if (lugrpNodeMap == null) {
            adapter_logger.warn("SNAAdapter:initAdapter> " + sLogStr + " lugrpNodeMap is NULL");    		
    		return 0;
    	}
    	else {
    		lugrpCount = getLuGrpNodeAndGroupMapCount();
    		
            adapter_logger.warn("SNAAdapter:initAdapter> " + sLogStr + " Success To Load LU Group Info, LU Group Count = " + lugrpCount);    		
    		printLuGroupInfo();
    		
    		return lugrpCount;
    	}
    }    
    
    /** 
     * 1. 기능 :  어댑터 그룹 이름, LU 그룹 이름별 세션 정보 저장 
     * 2. 처리 개요 : 
     *       - DB에서 어댑터 그룹 이름에 해당하는 LU 그룹 이름별 세션 정보를 hashmap에 저장한다.
     * 3. 주의사항
     * 
     * @param    sAdapterGrpName     어댑터 그룹 이름
     * @return   HashMap             어댑터 그룹 이름에 해당하는 LU 그룹 이름별 세션 정보
     **/
   	public HashMap loadAdapterInfo(String sAdapterGrpName, String sAdapterName) {
   		JDBCFactory      	factory   		= JDBCFactory.newInstance();
        SessionGroupJDBCEx 	jdbc      		= null;			
         
        String 				softlinkIpAddrs = null;
        String				localHostName = null;
        String				sStartBackupSession = "0";			// 백업 세션 시작 여부 
         
        ArrayList 			alSoftlinkIpAddr = null;
        ArrayList 			alSoftlinkHostName = new ArrayList();
         
        HashMap 			lugrpNodeTempMap	= new HashMap();
        
        String				logStr =  "[" + sAdapterGrpName + "]";
        
        try {
           	adapter_logger.warn("SNAAdapter:loadAdapterInfo> " + logStr + " Start To Load Adapter LU Session Group Info");

            jdbc = (SessionGroupJDBCEx)factory.create(SessionGroupJDBCEx.class);
         
            // WCA가 기동되는 로컬 호스트 이름을 구함
            localHostName = EAIServerManager.getInstance().getLocalServerHostName();
            
            // 어댑터 이름을 기준으로 프라퍼티 정보에서 SOFTLINK_IP 정보를 구함
            // 값이 없거나 1개 이상의 Softlink IP가 Comma로 구분되어 설정될 수 있음
            softlinkIpAddrs = getSoftlinkIpAddrProperty(sAdapterName);
            adapter_logger.warn("SNAAdapter:loadAdapterInfo> " + logStr + " SoftLink Property Value = " + softlinkIpAddrs);            
             
            // 20090810 Start
            // softlinkIpAddr = "127.0.0.1, 192.168.175.66 ";		// 테스트를 위한 임시값 설정
            // adapter_logger.warn("loadAdapterInfo> Temporary Softlink IP = " + softlinkIpAddr);
             
            // Softlink IP 주소 리스트를 토큰화 하고, 공백 제거
            alSoftlinkIpAddr = MyUtil.stringTokenizer(softlinkIpAddrs, ",");
            if (alSoftlinkIpAddr == null) {
            	throw new Exception("softlinkIpAddrs is NLL sAdapterName=" + sAdapterName);
            }
            
            for (int i = 0; i < alSoftlinkIpAddr.size(); i++) {
              	String		sTempSoftlinkIpAddr = "";
              	sTempSoftlinkIpAddr = (String)alSoftlinkIpAddr.get(i);
              	alSoftlinkIpAddr.set(i, sTempSoftlinkIpAddr.trim());
            }
             
            // Softlink IP 주소 리스트에한 Softlink 노드 이름을 구함
            for (int i = 0; i < alSoftlinkIpAddr.size(); i++) {            	            	
             	// IP에 해당하는 Softlink 호스트 이름을 구함
                String softlinkHostName = getSoftlinkHostName((String)alSoftlinkIpAddr.get(i));                
                if (softlinkHostName.compareTo(localHostName) == 0) {                    
                    alSoftlinkIpAddr.set(i, "127.0.0.1");
                }
                 
                alSoftlinkHostName.add(softlinkHostName);
                 
             	adapter_logger.warn("loadAdapterInfo> " + logStr + " [" + i + "] Softlink Host Name = " + (String)alSoftlinkHostName.get(i) + 
             			", IP = " + (String)alSoftlinkIpAddr.get(i));
            }

            // IP가 여러개 지정되어 있을 경우에는 백업 세션 관련 처리는 수행하지 않는다.
            if (alSoftlinkIpAddr.size() > 1) {
            	adapter_logger.warn("SNAAdapter:loadAdapterInfo> " + logStr + "] Set Forcely sStartBackupSessionFlag = 0");            	 
           	 	sStartBackupSession = "0";
            }
            else {
            	// 백업 세션 시작 여부 정보를 Property에서 읽어옴
            	sStartBackupSession = getStartBackupSessionProperty(sAdapterName);
            	adapter_logger.warn("SNAAdapter:loadAdapterInfo> " + logStr + " sStartBackupSessionFlag = " + sStartBackupSession);               	
            }

        	// Softlink IP 주소가 2개 이상인 경우에는 백업 모드 관련 기능을 사용할 수 없다는 플래그를 설정            
            boolean	bBackupSessionEnableFlag = true;
            if (alSoftlinkIpAddr.size() > 1) {
            	adapter_logger.warn("SNAAdapter:loadAdapterInfo> " + logStr + " bBackupSessionEnableFlag = " + bBackupSessionEnableFlag);               	
            	bBackupSessionEnableFlag = false;
            }
            
            for (int i = 0; i < alSoftlinkIpAddr.size(); i++) {
            	HashMap	lugrpGroupMap = new HashMap();

            	jdbc.getAdapterInfo(lugrpGroupMap, (String)alSoftlinkHostName.get(i), 
            			(String)alSoftlinkIpAddr.get(i), sAdapterGrpName, sStartBackupSession, bBackupSessionEnableFlag);
            	 
            	if (lugrpGroupMap != null) {
            		lugrpNodeTempMap.put((String)alSoftlinkHostName.get(i), lugrpGroupMap);
            	}
            }
            
           	adapter_logger.warn("SNAAdapter:loadAdapterInfo> " + logStr + " Success To Load Adapter LU Session Group Info");
            
            return lugrpNodeTempMap;            
        }
        catch (JDBCException e) {
            adapter_logger.error("SNAAdapter:loadAdapterInfo> " + logStr + ", Exception Msg = " + e.getMessage(), e);
            return null;
        }
        catch (java.net.UnknownHostException host) {
            adapter_logger.error("SNAAdapter:loadAdapterInfo> " + logStr + ", Exception Msg = " + host.getMessage(), host);
            return null;
        }
        catch (Exception ex) {
            adapter_logger.error("SNAAdapter:loadAdapterInfo> " + logStr + ", Exception Msg = " + ex.getMessage(), ex);
            return null;
        }
    }
   	
   	
    /** 
     * 1. 기능 : 어댑터 프라퍼티 중에서 SOFTLINK_IP 프라퍼티를 읽어들임
     * 2. 처리 개요 
     * 3. 주의사항
     * 
     * @param    adapterName     어댑터 이름
     * 
     * @return   어댑터 이름에 해당하는 Softlink IP Property 정보
     **/   	
    public String getSoftlinkIpAddrProperty(String adapterName) {
        String 				softlinkIpAddrProperty = "";
        String				propertyName = "SOFTLINK_IP";
        
       	adapter_logger.warn("SNAAdapter:getSoftlinkIpAddrProperty> PROPERTY_GROUP = " + adapterName + ", PROPERTY = " + propertyName);
        
       	softlinkIpAddrProperty = AdapterPropManager.getInstance().getProperty(adapterName, propertyName);
        
        // Property가 설정되어 있지 않은 경우, 로컬 호스트 이름과 로컬 IP 주소(127.0.0.1) 사용
        if (softlinkIpAddrProperty == null) {
   			adapter_logger.warn("SNAAdapter:getSoftlinkIpAddrProperty> [" + adapterName + "] softlinkIpAddr is NULL, So Set To 127.0.0.1");
   			softlinkIpAddrProperty = "127.0.0.1";
        }
        // Property 값이 유효하지 않은 경우, 로컬 호스트 이름과 로컬 IP 주소(127.0.0.1) 사용      
        else if (softlinkIpAddrProperty.trim().length() == 0) {
       		if (adapter_logger.isInfo()) {
       			adapter_logger.info("SNAAdapter:getSoftlinkIpAddrProperty> [" + adapterName + "] softlinkIpAddr Length is 0, So Set To 127.0.0.1");
       		}

       		softlinkIpAddrProperty = "127.0.0.1";
        }
 
        adapter_logger.info("SNAAdapter:getSoftlinkIpAddrProperty> [" + adapterName + "] softlinkIpAddr = " + softlinkIpAddrProperty);
        
        return softlinkIpAddrProperty.trim();
    }   	
   	
    /** 
     * 1. 기능 : Softlink IP 주소에 해당하는 Softlink Node 이름을 구함
     * 2. 처리 개요 
     * 3. 주의사항
     * 
     * @param    softlinkIpAddr     Softlink IP 주소
     * 
     * @return   Softlink Node 이름
     **/       
    public String getSoftlinkHostName(String softlinkIpAddr) {
    	String 				softlinkHostName = "";
    	
        JDBCFactory      	factory   		= JDBCFactory.newInstance();
        SessionGroupJDBCEx 	jdbc      		= null;
        String				localHostName 	= "";
        
       	adapter_logger.warn("SNAAdapter:getSoftlinkHostName> Softlink IP Address = " + softlinkIpAddr);
        
    	try {        
    		localHostName = EAIServerManager.getInstance().getLocalServerHostName();
	    	if (softlinkIpAddr.compareTo("127.0.0.1") == 0) {
	    		adapter_logger.info("SNAAdapter:getSoftlinkHostName> IP is Local Loopback IP Address, So Return Local HostName, HostName = " + localHostName);	    		
	    		return localHostName;
	    	}
    	       
            jdbc = (SessionGroupJDBCEx)factory.create(SessionGroupJDBCEx.class);
        
           	adapter_logger.info("SNAAdapter:getSoftlinkHostName> Try to Get Softlink Host Name for IP " + softlinkIpAddr);
            
    		softlinkHostName = jdbc.getSoftlinkNodeName(softlinkIpAddr);
    		
        	// 소프트링크 호스트 이름을 가져오지 못한 경우, 로컬 호스트 이름과 로컬 IP 주소(127.0.0.1) 사용
        	if (softlinkHostName == null) {
                adapter_logger.warn("SNAAdapter:getSoftlinkHostName> [" + softlinkIpAddr + "] softlinkHostName is NULL, So Return Local HostName, HostName = " + localHostName);
                return localHostName;
        	}  
        	
        	if (softlinkHostName.length() == 0) {
                adapter_logger.warn("SNAAdapter:getSoftlinkHostName> [" + softlinkIpAddr + "] softlinkHostName Length is 0, So Return Local HostName, HostName = " + localHostName);
                return localHostName;                
        	}        	
        	
           	adapter_logger.warn("SNAAdapter:getSoftlinkHostName> [" + softlinkIpAddr + "] softlinkHostName = " + softlinkHostName);
            
        	return softlinkHostName;        	
    	}
        catch (JDBCException e) {
            adapter_logger.warn("SNAAdapter:getSoftlinkHostName> JDBCException Occur, softlinkIpAddr = " + softlinkIpAddr, e);            
            adapter_logger.error(e.getMessage(), e);
            return localHostName;
        }    	
        catch (java.net.UnknownHostException host) {
            adapter_logger.warn("SNAAdapter:getSoftlinkHostName> UnknownHostException Occur, softlinkIpAddr = " + softlinkIpAddr, host);            
            adapter_logger.error(host.getMessage(), host);           	            
            return localHostName;
        }        
    }   	
   	
 	
   	
    /** 
     * 1. 기능 : 어댑터에 속한 세션 Pool을 초기화하고 세션 Pool을 시작
     * 2. 처리 개요 : 
     * 3. 주의사항
     * @param  sGroupCode  LU 그룹 이름
     **/
    public void startAdapter() {
        SessionGroupDataEx 		lugrpInfo = null;			// LU 그룹 정보
        HashMap					lugrpGroupMap = null;		// LU 그룹 정보 맵
         
        try {           
 	        // SNAAdapter에 대한 LU 그룹 노드 맵을 돌면서 처리
            Iterator iterNode = lugrpNodeMap.keySet().iterator();
            while (iterNode.hasNext()) {
            	String sNodeName = (String)iterNode.next();
                adapter_logger.warn("SNAAdapter:startAdapter> [" + adapterGroupName + "] Start Process LU Group Info, NodeName = " + sNodeName);
                	
                lugrpGroupMap = (HashMap)lugrpNodeMap.get(sNodeName);
                if (lugrpGroupMap == null) {
                	adapter_logger.warn("SNAAdapter:startAdapter> [" + adapterGroupName + "] LU GROUP Node Map is NULL, NodeName = " + sNodeName);                	
                 	continue;                        
                }
                 
                // SNAAdapter에 대한 LU 그룹 노드에 대한 LU 그룹 맵을 돌면서 처리
                Iterator iterGroup = lugrpGroupMap.keySet().iterator();
                while (iterGroup.hasNext()) {
                 	String sGroupCode = (String)iterGroup.next();
                    adapter_logger.warn("SNAAdapter:startAdapter> [" + adapterGroupName + "] Start Process LU Group Info, NodeName = " + sNodeName + 
                    		", GroupCode = " + sGroupCode);
                    	
                    lugrpInfo = (SessionGroupDataEx)lugrpGroupMap.get(sGroupCode);
                 	if (lugrpInfo == null) {
                       	adapter_logger.warn("SNAAdapter:startAdapter> [" + adapterGroupName + "] LU GROUP Map is NULL, NodeName = " + sNodeName + 
                       			", GroupCode = " + sGroupCode);
                 		continue;
                 	}
                 	
                 	// 로컬 및 백업 세션 풀 정보를 초기화하고, 세션 풀에 속한 세션을 시작 시킴
                 	// 백업 세션풀은 START_BACKUP_SESSION 프라퍼티에 따라 시작 여부가 결정됨.
                 	initAndStartLocalAndBackupSessionPool(lugrpInfo);
                }
            }
 	        return;
        }
        catch (Exception e) {
            adapter_logger.error("SNAAdapter:startAdapter> [" + adapterGroupName + "] @@@ Error :" + e.toString(), e);
            return;
        }
    } 

    /** 
     * 1. 기능 : 로컬 및 백업 세션 풀 정보를 초기화하고, 세션 풀에 속한 세션을 시작 시킴
     * 2. 처리 개요 : 
     * 3. 주의사항 
     *
     * @param 	lugrpInfo LU 그룹 정보  
     **/    

    public void initAndStartLocalAndBackupSessionPool(SessionGroupDataEx lugrpInfo) {
    	String nodeNmae = (String)lugrpInfo.getNodeName();
    	String lugrpName = (String)lugrpInfo.getGroupName();
    	String sLogStr = "[" + adapterGroupName + "]";
    	String sSessPoolInfo =  "[" + nodeNmae + "][" + lugrpName + "]";
    	
    	// 세션 Pool 정보 초기화 및 설정
	    adapter_logger.warn("SNAAdapter:initAndStartLocalAndBackupSessionPool> " + sLogStr + " Init Session Pool Info, Session Pool = " + sSessPoolInfo);                  
	    initSessionPoolInfo(lugrpInfo, true);
	
	    // 세션 풀에 속한 세션을 start 시킨다.
	    adapter_logger.warn("SNAAdapter:initAndStartLocalAndBackupSessionPool> " + sLogStr + " Start Session Pool, Session Pool = " + sSessPoolInfo);                    
	    startSessionPool(true);
	     
	    // 백업 세션 정보 확인
	 	if (lugrpInfo.getBackupNodeName().equals("") || lugrpInfo.getBackupPort() == 0 || lugrpInfo.getSessionCnt() == 0) {
	      	adapter_logger.warn("SNAAdapter:initAndStartLocalAndBackupSessionPool> " + sLogStr + " Invalid Backup Session Info, So Don't Initialize Backup Session Pool, Session Pool = " + sSessPoolInfo);                   		
	      	return;
	 	}
	 
	 	/// 백업 세션 정보 초기화 및 설정
	 	adapter_logger.warn("SNAAdapter:initAndStartLocalAndBackupSessionPool>  " + sLogStr + " Init Backup Session Pool Info, Session Pool = " + sSessPoolInfo);                	
	 	initSessionPoolInfo(lugrpInfo, false);
	 	
	    if (lugrpInfo.getStartBackupSession().equals("1")) {        	
	       	adapter_logger.warn("SNAAdapter:initAndStartLocalAndBackupSessionPool> " + sLogStr + " Start Backup Session Pool, Session Pool = " + sSessPoolInfo);                       	
	       	startSessionPool(false);
	    }                    	
	    else {
	     	adapter_logger.warn("SNAAdapter:initAndStartLocalAndBackupSessionPool> " + sLogStr + " Don't Start Backup Session Pool, Session Pool = " + sSessPoolInfo);                    	
	    }
    }    
   
    /** 
     * 1. 기능 :  어댑터 그룹에 속한 LU 그룹과 현재 설정된 백업 모드 정보 반환
     * 2. 처리 개요 : 
     * 3. 주의사항 
     *
     * @return   LU 그룹 및 백업 모드 정보 [
     *           Key : LU 그룹 이름, value : Boolean Object 백업 모드 정보 (true : 백업, false : 정상)
     **/
    public HashMap getLuGrpBackupModeMap() throws WCAException {
        boolean bBackupMode = false;
        int		nSessNum = 0;        
        HashMap lugrpNodeAndGroupBackupModeMap = new HashMap();
        
        if (lugrpNodeMap == null) {
        	return lugrpNodeAndGroupBackupModeMap;
        }
        
        // LU 그룹 노드맵에서 키에 대한 Iterator를 구함
        Iterator iterNode = lugrpNodeMap.keySet().iterator();
             
        // LU 그룹 노드맵 키에 대한 Iterator를 돌면서 처리
        while (iterNode.hasNext()) {
            // LU 그룹 노드맵에서 노드 이름을 구함
            String sNodeName = (String)iterNode.next();
             	
            // LU 그룹 노드맵에서 노드 이름을 기준으로 LU 그룹 맵을 구함
            HashMap lugrpGroupMap = (HashMap)lugrpNodeMap.get(sNodeName);
            if (lugrpGroupMap == null) {
            	adapter_logger.info("SNAAdapter:getLuGrpBackupModeMap> Node에 대한 LU 그룹 맵이 없음, NodeName = " + sNodeName);            		
             	continue;
            }
             	
            // LU 그룹별 백업 모드 정보를 저장할 임시 HashMap 오브젝트 생성
            HashMap tempHashMap = new HashMap();
             	
            // LU 그룹 맵에서 키에 대한 Iterator를 구함
            Iterator iterGroup = lugrpGroupMap.keySet().iterator();
             	
            // LU 그룹맵의 키에 대한 Iterator를 돌면서 처리
            while (iterGroup.hasNext()) {
             	// LU 그룹 맵에서 LU 그룹 값을 구함
             	String sLuGroupName = (String)iterGroup.next();
             		
             	// LU 그룹의 백업 모드 정보를 구함
             	bBackupMode = getBackupMode(sNodeName, sLuGroupName);

             	// LU 그룹의 백업 모드 정보를 구함
             	nSessNum = getConnectedSessNum(sNodeName, sLuGroupName);
             	
             	// adapter_logger.error("SNAAdapter:getLuGrpBackupModeMap> [" + sNodeName + "][" + sLuGroupName + "] Session Count = " + nSessNum);
             	
             	// 임시 HashMap에 저장
             	/*
             	if(bBackupMode) 
             		tempHashMap.put(sLuGroupName, "TRUE");
             	else
             		tempHashMap.put(sLuGroupName, "FALSE");
             	*/
             	
             	// "세션 상태":"백업 상태"
             	if (nSessNum > 0) {             	
	             	if (bBackupMode) { 
	             		tempHashMap.put(sLuGroupName, "TRUE:TRUE");
	             	}
	             	else {
	             		tempHashMap.put(sLuGroupName, "TRUE:FALSE");
	             	}
             	}
             	else {
	             	if (bBackupMode) { 
	             		tempHashMap.put(sLuGroupName, "FALSE:TRUE");
	             	}
	             	else {
	             		tempHashMap.put(sLuGroupName, "FALSE:FALSE");
	             	}
             	}
            }
             	
            // 임시 HashMap을 리턴할 HashMap에 저장
            lugrpNodeAndGroupBackupModeMap.put(sNodeName, tempHashMap);
        }
         
        // printLuGrpBackModeMap(lugrpNodeAndGroupBackupModeMap);
        
        return lugrpNodeAndGroupBackupModeMap;
    } 
   	
    /** 
     * 1. 기능 :  설정된 백업 모드 정보 출력
     * 2. 처리 개요 : 
     * 3. 주의사항 
     *
     * @param    luGroupMode     백업 모드 정보
     **/
    public void printLuGrpBackModeMap(HashMap lugrpNodeAndGroupBackupModeMap) {
    	Iterator iterNode = lugrpNodeAndGroupBackupModeMap.keySet().iterator();
         
        if (adapter_logger.isDebug()) {
        	adapter_logger.debug("SNAAdapter:printLuGrpBackModeMap> [" + adapterGroupName + "] Start Print LU Group Backup Mode");
        }
         
        while (iterNode.hasNext()) {
            String sNodeName = (String)iterNode.next();
            HashMap lugrpGroupBackupModeMap = (HashMap)lugrpNodeAndGroupBackupModeMap.get(sNodeName);
            if (lugrpGroupBackupModeMap == null) { 
            	continue;
            }
             
            Iterator iterGroup = lugrpGroupBackupModeMap.keySet().iterator();
            while (iterGroup.hasNext()) {
            	String sLuGroupName = (String)iterGroup.next();
            	String sValue  = (String)lugrpGroupBackupModeMap.get(sLuGroupName);
            	
                if (adapter_logger.isDebug()) {
                	adapter_logger.debug("SNAAdapter:printLuGrpBackModeMap> [" + adapterGroupName + "] " + sNodeName + "-" + sLuGroupName + "=" + sValue);
                }
            }
        }

        if (adapter_logger.isDebug()) {
        	adapter_logger.debug("SNAAdapter:printLuGrpBackModeMap> [" + adapterGroupName + "] End Print LU Group Backup Mode");
        }        
    }
    

    /** 
     * 1. 기능 :  해당되는 LU 세션 풀 중지
     * 2. 처리 개요 : 
     *    - 세셔 그룹에 속한 thread를 종료시킨다.
     *    - Thread Group에 속한 thread가 종료할 때까지 기다린다.
     *    - Thread Group을 Thread Group 리스트에서 삭제한다.
     *    - 세션 풀 리스트 삭제한다.
     * 3. 주의사항
     * 
     * @param  sessgroup SessionGroupData object
     **/
    public void stopSessionPoolEx(SessionGroupDataEx sessgroup) {
         String sLuGroupName = sessgroup.getGroupName();
         String sNodeName = sessgroup.getNodeName();
         
         int				i = 0;
         WCASessionPool		wcaSessionPool;
         
         // 1. Thread 종료
         for (i = 0; i < listWCALocalSessonPool.size(); i++) {
             wcaSessionPool = (WCASessionPool)listWCALocalSessonPool.get(i);
             if (sNodeName.equals(wcaSessionPool.getNodeName())) {
            	 if (sLuGroupName.equals(wcaSessionPool.getGroupCode())) {
            		 wcaSessionPool.stopAdapter();
            	 }
             }
     	}
     
 	    for (i = 0; i < listWCABackupSessonPool.size(); i++) {
 	        wcaSessionPool = (WCASessionPool)listWCABackupSessonPool.get(i);
 	        if (sNodeName.equals(wcaSessionPool.getNodeName())) {
 	        	if (sLuGroupName.equals(wcaSessionPool.getGroupCode())) {
 	        		wcaSessionPool.stopAdapter();
 	        	}
 	        }
 	    }
         
        // 2. Thread 종료할 때까지  대기한다.
 	    if (threadNodeMap == null) {
 	    	return;
 	    }
        
 	    HashMap threadGroupMap = (HashMap)threadNodeMap.get(sNodeName);
        if (threadGroupMap == null) {
            adapter_logger.warn("SNAAdapter:stopSessionPoolEx> " + adapterGroupName + " THREAD GROUPS IS NULL");
            return;
        }
         
        ThreadGroup threadGroup = (ThreadGroup)threadGroupMap.get(sLuGroupName);
        waitForThreadEnd(threadGroup);
         
        // 3. Thread Group haspmap에서 삭제한다.
        deleteThreadGroup(sNodeName, sLuGroupName);
         
         // 4. 세션 풀 리스트 삭제한다.
        deleteSessionPool(sNodeName, sLuGroupName);
    }
     
  
    /** 
     * 1. 기능 :  LU 그룹에 해당하는 세션 풀 삭제
     * 2. 처리 개요 : 
     * 3. 주의사항
     * 
     * @param    sGroupCode      LU 그룹 이름
     **/
    public void deleteSessionPool(String sNodeName, String sGroupCode) {
        int					i = 0;
        WCASessionPool		wcaSessionPool = null;
                
        if (listWCALocalSessonPool.size() != 0) {
            for (i = 0; i < listWCALocalSessonPool.size(); i++) {
                wcaSessionPool = (WCASessionPool)listWCALocalSessonPool.get(i);
                if (wcaSessionPool.getNodeName().equals(sNodeName)) {
                	if (wcaSessionPool.getGroupCode().equals(sGroupCode)) {
                		listWCALocalSessonPool.remove(wcaSessionPool) ;
                	}
                }
            }
        }
        
        if (listWCABackupSessonPool.size() != 0) {
            for (i = 0; i < listWCABackupSessonPool.size(); i++) {
                wcaSessionPool = (WCASessionPool)listWCABackupSessonPool.get(i);
                if (wcaSessionPool.getNodeName().equals(sNodeName)) {
                	if (wcaSessionPool.getGroupCode().equals(sGroupCode)) {
                		listWCABackupSessonPool.remove(wcaSessionPool) ;
                	}
                }
            }
        }
        
        return;
    }
    
    /** 
     * 1. 기능 :  어댑터 그룹에 해당하는 모든 LU 그룹 세션을 중지
     * 2. 처리 개요 : 
     *    - 세셔 그룹에 속한 thread를 종료시킨다.
     *    - Thread Group에 속한 thread가 종료할 때까지 기다린다.
     * 3. 주의사항
     **/
    public void stopAdapter() {
        int					i = 0;
        WCASessionPool		wcaSessionPool;
        
        adapter_logger.warn("SNAAdapter:stopAdapter> [" + adapterGroupName + "] Try To Stop Adapter");
        adapter_logger.warn("SNAAdapter:stopAdapter> [" + adapterGroupName + "] listWCALocalSessonPool size = " + listWCALocalSessonPool.size());
        adapter_logger.warn("SNAAdapter:stopAdapter> [" + adapterGroupName + "] listWCABackupSessonPool size = " + listWCABackupSessonPool.size());
                
        // 1. Thread 종료
        adapter_logger.warn("SNAAdapter:stopAdapter> [" + adapterGroupName + "] Start To Stop Local Session Pool");        
        for (i = 0; i < listWCALocalSessonPool.size(); i++) {
            wcaSessionPool = (WCASessionPool)listWCALocalSessonPool.get(i);
        	wcaSessionPool.stopAdapter();
            //trc_logger.info(wcaSessionPool.toString() + " is stopping" );
    	}
        adapter_logger.warn("SNAAdapter:stopAdapter> [" + adapterGroupName + "] End To Stop Local Session Pool");        
    
        adapter_logger.warn("SNAAdapter:stopAdapter> [" + adapterGroupName + "] Start To Stop Backup Session Pool");        
	    for (i = 0; i < listWCABackupSessonPool.size(); i++) {
	        wcaSessionPool = (WCASessionPool)listWCABackupSessonPool.get(i);
	    	wcaSessionPool.stopAdapter();
            //trc_logger.info(wcaSessionPool.toString() + " is stopping" );
		}
	    adapter_logger.warn("SNAAdapter:stopAdapter> [" + adapterGroupName + "] End To Stop Backup Session Pool");	    
                
        // 2. Thread 종료할 때까지  대기한다.
        printThreadInfo();
        if (threadNodeMap == null) {
        	adapter_logger.warn("SNAAdapter:stopAdapter> [" + adapterGroupName + "] threadNodeMap is NULL, So Return");
        	return;            
        }
                    
        // 어댑터 그룹에 속한 노드별 LU 그룹별 쓰레드를 종료하는 것을 대기
        Iterator iterNode = threadNodeMap.keySet().iterator();
        while (iterNode.hasNext()) {
        	String 	sNodeName = (String)iterNode.next();
        	HashMap threadGroupMap = (HashMap)threadNodeMap.get(sNodeName);
        	       	
            ThreadGroup threadGroup = null;			// 노드별, LU 그룹별 쓰레드 그룹
            String 		sLuGroupName  = null;		// 노드별, LU 그룹 코드
            
            Iterator iterGroup = threadGroupMap.keySet().iterator();
            while (iterGroup.hasNext()) {
            	sLuGroupName = (String)iterGroup.next();                
                threadGroup = (ThreadGroup)threadGroupMap.get(sLuGroupName);
                
                waitForThreadEnd(threadGroup);
            }  
        }
        
        adapter_logger.warn("SNAAdapter:stopAdapter> [" + adapterGroupName + "] Clear WCA Session Pool List");        
        listWCALocalSessonPool.clear();
        listWCABackupSessonPool.clear();  
        
        adapter_logger.warn("SNAAdapter:stopAdapter> [" + adapterGroupName + "] Clear Thread Node Map");
        threadNodeMap.clear();
        /*
        iterNode = threadNodeMap.keySet().iterator();
        while(iterNode.hasNext()) {
        	String sNodeName = (String)iterNode.next();
        	threadNodeMap.remove(sNodeName);
        }
        */
        
        nLocalSessionPoolIdx = 0;
        
        adapter_logger.warn("SNAAdapter:stopAdapter> [" + adapterGroupName + "] Clear LU Group Node Map");
        lugrpNodeMap.clear();
        /*
        iterNode = lugrpNodeMap.keySet().iterator();
        while(iterNode.hasNext()) {
        	String sNodeName = (String)iterNode.next();
        	lugrpNodeMap.remove(sNodeName);
        }        
        */
        adapter_logger.warn("SNAAdapter:stopAdapter> [" + adapterGroupName + "] Success To Stop Adapter");
    }
    
    
    /** 
    * 1. 기능 :  Thread Group에 속한 Thread가 종료될때 까지 대기한다.
    * 2. 처리 개요 : 
    *    - Thread Group에 속한 thread가 종료할 때까지 기다린다.
    * 3. 주의사항
    * 
    * @param threadGroup Thread Group
    **/
    public void waitForThreadEnd(ThreadGroup threadGroup) {
    	int		nSleepCount = 0;
    	
    	adapter_logger.warn("SNAAdapter:waitForThreadEnd> [" + adapterGroupName + "] Start To Wait Thread End, Thread Group Name = " + threadGroup.getName());
    	adapter_logger.warn("SNAAdapter:waitForThreadEnd> [" + adapterGroupName + "] Thread Group Name = " + threadGroup.getName() + ", Thread Count = " + threadGroup.activeCount());
    	        
        while (threadGroup.activeCount() > 0) {
            try {
                // 20190927 START                
                nSleepCount++;                
                // 20190927 END
                
            	adapter_logger.warn("SNAAdapter:waitForThreadEnd> [" + adapterGroupName + "] Waiting,  Thread Group Name = " + threadGroup.getName() + ", Thread Count = " + threadGroup.activeCount());            	                
                Thread.sleep(1000);

                // 20190927 START
                // 무한정 대기를 방지하기 위해서 10초 이상 대기시  interrupt 호출, 일단 원인 파악을 위해서 막음
                /*
                if (nSleepCount == 10) {
                	adapter_logger.warn("SNAAdapter:waitForThreadEnd> [" + adapterGroupName + "] Call interrupt()");
                	threadGroup.interrupt();
                	printThreadInfo();                	
                }
                */
                // 20190927 END
                
                // 20200724 Start
                if (nSleepCount >= 15) {
                	adapter_logger.warn("SNAAdapter:waitForThreadEnd> [" + adapterGroupName + "] Call interrupt()");
                	threadGroup.interrupt();
                	printThreadInfo();                	
                }
                // 20200724 End                
                
                // 20190927 START                
                // 종료 중 생성된 쓰레드가 생성된 것을 대비해서 한번더 호출
                if ((nSleepCount % 10) == 0) {
	                int				i;
	                WCASessionPool	wcaSessionPool;
	                
	                adapter_logger.warn("SNAAdapter:waitForThreadEnd> [" + adapterGroupName + "] nSleepCount = " + nSleepCount);
	                adapter_logger.warn("SNAAdapter:waitForThreadEnd> [" + adapterGroupName + "] Try To Stop Adapter");
	                adapter_logger.warn("SNAAdapter:waitForThreadEnd> [" + adapterGroupName + "] listWCALocalSessonPool size = " + listWCALocalSessonPool.size());
	                adapter_logger.warn("SNAAdapter:waitForThreadEnd> [" + adapterGroupName + "] listWCABackupSessonPool size = " + listWCABackupSessonPool.size());
	                        
	                // 1. Thread 종료
	                adapter_logger.warn("SNAAdapter:waitForThreadEnd> [" + adapterGroupName + "] Start To Stop Local Session Pool");        
	                for(i = 0; i < listWCALocalSessonPool.size(); i++) {
	                    wcaSessionPool = (WCASessionPool)listWCALocalSessonPool.get(i);
	                	wcaSessionPool.stopAdapter();
	                    //trc_logger.info(wcaSessionPool.toString() + " is stopping" );
	            	}
	                adapter_logger.warn("SNAAdapter:waitForThreadEnd> [" + adapterGroupName + "] End To Stop Local Session Pool");        
	            
	                adapter_logger.warn("SNAAdapter:waitForThreadEnd> [" + adapterGroupName + "] Start To Stop Backup Session Pool");        
	        	    for(i = 0; i < listWCABackupSessonPool.size(); i++) {
	        	        wcaSessionPool = (WCASessionPool)listWCABackupSessonPool.get(i);
	        	    	wcaSessionPool.stopAdapter();
	                    //trc_logger.info(wcaSessionPool.toString() + " is stopping" );
	        		}
	        	    adapter_logger.warn("SNAAdapter:waitForThreadEnd> [" + adapterGroupName + "] End To Stop Backup Session Pool");
                }
                // 20190927 END
            } 
            catch(Exception e) {
            	adapter_logger.warn("SNAAdapter:waitForThreadEnd> ", e); 
            }   
        }
        
        adapter_logger.warn("SNAAdapter:waitForThreadEnd> [" + adapterGroupName + "] End To Wait Thread End, Thread Group Name = " + threadGroup.getName());        
        try {
            threadGroup.destroy();
        } 
        catch (Exception e) {
        	adapter_logger.warn("SNAAdapter:waitForThreadEnd> ", e);
        }
    }
    

    /** 
     * 1. 기능 :  LU 세션 풀 정보 초기화 및 설정
     * 2. 처리 개요 : 
     *    - WCASessionPool 생성 및 정보 값을 설정한다.
     *    - WCASessionPool start 시킨다.
     *    - listWCALocalSessonPool에 추가한다.
     * 3. 주의사항
     * 
     * @param  sessgroup SessionGroupData object
     **/
    public void initSessionPoolInfo(SessionGroupDataEx sessgroup, boolean isLocalSession) {
    	WCASessionPool wcaSessionPool= null;

        String  sGroupIp         = null; 

        if (isLocalSession) {
            sGroupIp = sessgroup.getSoftLinkSevrName();
            wcaSessionPool = new WCASessionPool(sGroupIp, sessgroup.getPort(), sessgroup.getSessionCnt());
            wcaSessionPool.setBackupCvsInfo(sessgroup.getBackupCvsMode());
        }
        else {
            wcaSessionPool = new WCASessionPool(sessgroup.getBackupNodeName(), sessgroup.getBackupPort(), sessgroup.getSessionCnt());
            wcaSessionPool.setSessionPoolInfo(adapterGroupName, adapterName, sessgroup.getGroupName());
            wcaSessionPool.setBackupSessionPool(true);        	 
        }

        wcaSessionPool.setSessionPoolInfo(adapterGroupName, adapterName, sessgroup.getGroupName());
        
        wcaSessionPool.setLogInfo(sessgroup.isLogWritable(), sessgroup.getLogLevel(), sessgroup.getLogSize() );
        wcaSessionPool.setTranTimeout(sessgroup.getTranTimeout());
         
        wcaSessionPool.setNodeName(sessgroup.getNodeName());
        wcaSessionPool.setBackupEnableFlag(sessgroup.getBackupEnableFlag());
                  
        if (isLocalSession) {
       		 listWCALocalSessonPool.addLast(wcaSessionPool);
        }
        else {
       		listWCABackupSessonPool.addLast(wcaSessionPool);       	 
        }
    }
        

    /** 
     * 1. 기능 :  세션 시작 
     * 2. 처리 개요 : 
     * 3. 주의사항
     **/
    public void startSessionPool(boolean isLocalSession) {
        WCASessionPool		wcaSessionPool = null;
        
        // 로컬 세션인 경우
        if (isLocalSession) {
            for (int i = 0; i < listWCALocalSessonPool.size(); i++) {
            	wcaSessionPool = (WCASessionPool)listWCALocalSessonPool.get(i);   
                if (!wcaSessionPool.isStarted()) {
                	wcaSessionPool.initAndStartWcaSessionInSessionPool();
                }
            }    
        } 
        // 백업 세션인 경우
        else {
	        for (int i = 0; i < listWCABackupSessonPool.size(); i++) {
	            wcaSessionPool = (WCASessionPool)listWCABackupSessonPool.get(i);   
	            if (!wcaSessionPool.isStarted()) {
	                wcaSessionPool.initAndStartWcaSessionInSessionPool();
	            }
	        }        	
        }
    }
    
    public void startSessionPool(String sNodeName, String sGroupCode) {
        try {
            WCASessionPool		wcaSessionPool = getLocalSessionPool(sNodeName, sGroupCode);
            if(wcaSessionPool != null) {
            	adapter_logger.warn("SNAAdapter:startSessionPool> " + wcaSessionPool.toString() + " startSessionPool....");
                if (!wcaSessionPool.isStarted()) {
                    wcaSessionPool.initAndStartWcaSessionInSessionPool();
                }
            }            
        }
        catch (WCAException e) {            
            adapter_logger.error(e.toString(), e);
        }
    }
   
        
    /** 
     * 1. 기능 :  세션 증가
     * 2. 처리 개요 : 증가한 세션수만큼 증가시킨다.
     *     - 백업 풀 : 서버와 port가 변경되지않았을 경우 
     *     - etc    : port가 변경되지않았을 경우 
     * 3. 주의사항
     * @param  sessPool  WCASessionPool object
     * @param  sessgroup SessionGroupData object
     **/
    public void increaseSessionPoolEx(WCASessionPool sessPool, SessionGroupDataEx newGroup) {
        int nOldSessNum = sessPool.getSessNum();
        int nNewSessNum = newGroup.getSessionCnt();
        
        // 백업 풀인 경우
        if (sessPool.isBackupSessionPool()) {
            if (sessPool.getHostName().equals(newGroup.getBackupNodeName()) &&
            		sessPool.getPort() == newGroup.getPort() && nOldSessNum < nNewSessNum) {
            	sessPool.increaseSessionPool(nOldSessNum, nNewSessNum);
            }
        }
        else {
            if (sessPool.getPort() == newGroup.getPort() && nOldSessNum < nNewSessNum) {
                sessPool.increaseSessionPool(nOldSessNum, nNewSessNum);   
            }
        }
    }

    
    /** 
    * 1. 기능 :  백업 세션 시작 
    * 2. 처리 개요 : 
    *     - 백업 세션이 시작되지 않았으면 시작시킨다.
    * 3. 주의사항
    * @param  sGroupCode  LU 그룹 이름
    **/
    public void startBackupSessionPool(String sNodeName, String sGroupCode) {
        try {
            WCASessionPool wcaSessionPool = getBackupSessionPool(sNodeName, sGroupCode);
            
            // 백업 모드이고 백업 세션이 시작되지 않았으면 시작시킨다.            
            if (!wcaSessionPool.getBackupMode() && !wcaSessionPool.isStarted()) {
            	adapter_logger.warn("SNAAdapter:startBackupSessionPool> " + wcaSessionPool.toString() + " startBackupSessionPool....");
                wcaSessionPool.initAndStartWcaSessionInSessionPool();
            }            
        }
        catch (WCAException e) {
            if (adapter_logger.isError()) {
                adapter_logger.error("WCAException", e);
            }
        }
    }
    

    
    /** 
    * 1. 기능 :  어댑터 그룹 이름별로 SoftLink와 설정된 세션 수 반환
    * 2. 처리 개요 : 
    * 3. 주의사항
    **/
    public int getConfiguredSessNum() {
        int					nTotalSessNum = 0;
        
        nTotalSessNum = getConfiguredNormalSessNum();
        nTotalSessNum += getConfiguredBackupSessNum();
        
        return nTotalSessNum;
    }    
    
    public int getConfiguredNormalSessNum() {
        return getConfiguredSessNum(listWCALocalSessonPool, "", "");
    }  
    
    public int getConfiguredBackupSessNum() {
       return getConfiguredSessNum(listWCABackupSessonPool, "", "");
    }
    
    public int getConfiguredSessNum(String sNodeName, String sGroupCode) {
        int					nTotalSessNum = 0;
        
        nTotalSessNum = getConfiguredNormalSessNum(sNodeName, sGroupCode);
        nTotalSessNum += getConfiguredBackupSessNum(sNodeName, sGroupCode);
      
        return nTotalSessNum;
    }    
    
    public int getConfiguredNormalSessNum(String sNodeName, String sGroupCode) {
        return getConfiguredSessNum(listWCALocalSessonPool, sNodeName, sGroupCode);
    }    
    
    public int getConfiguredBackupSessNum(String sNodeName, String sGroupCode) {
       return getConfiguredSessNum(listWCABackupSessonPool, sNodeName, sGroupCode);
    }
    
   
   /** 
    * 1. 기능 :  어댑터 그룹 이름별, LU 그룹 이름별로 SoftLink와 설정된 세션 수 반환
    * 2. 처리 개요 : 
    * 3. 주의사항
    * 
    * @param sGroupCode LU 그룹 이름
    **/
    private int getConfiguredSessNum(LinkedList sesspool, String sNodeName, String sGroupCode) {
        WCASessionPool		wcaSessionPool = null;
        int					nTotalSessNum = 0;
        int                 nSessNum = 0;
        
        if (sesspool.size() != 0) {
            for (int i = 0; i < sesspool.size(); i++) {
                wcaSessionPool = (WCASessionPool)sesspool.get(i);
                nSessNum = wcaSessionPool.getConfiguredSessNum();
                if (!sGroupCode.equals("")) {
                	// 20110411 Start mjhong
                	/*
                    if (wcaSessionPool.getGroupCode().equals(sGroupCode))
                        return nSessNum;
                    */
                	if (wcaSessionPool.getNodeName().equals(sNodeName)) {
                		if (wcaSessionPool.getGroupCode().equals(sGroupCode)) {                    
                			return nSessNum;
                		}
                	}
                	// 20110411 End mjhong
                }
                else {
                    nTotalSessNum += nSessNum;
                }
            }
        }
        return nTotalSessNum;
    }
    
    /** 
    * 1. 기능 :  어댑터 그룹 이름별로 SoftLink와 연결된 세션수 반환
    * 2. 처리 개요 : 
    * 3. 주의사항
    **/
    public synchronized int getConnectedSessNum() {
        int	nTotalSessNum = 0;
        
        nTotalSessNum = getConnectedNormalSessNum();
        nTotalSessNum += getConnectedBackupSessNum();
        
        return nTotalSessNum;
    }
    
    public int getConnectedNormalSessNum() {
        return getConnectedSessNum(listWCALocalSessonPool);
    }    
    
    public int getConnectedBackupSessNum() {
       return getConnectedSessNum(listWCABackupSessonPool);
    }
    
    /** 
    * 1. 기능 :  어댑터 그룹 이름별, LU 그룹 이름별로 SoftLink와 연결된 세션수 반환
    * 2. 처리 개요 : 
    * 3. 주의사항
    * 
    * @param sGroupCode LU 그룹 이름
    **/    
    public int getConnectedSessNum(String sNodeName, String sGroupCode) {
        int					nTotalSessNum = 0;
        
        nTotalSessNum = getConnectedNormalSessNum(sNodeName, sGroupCode);
        nTotalSessNum += getConnectedBackupSessNum(sNodeName, sGroupCode);
      
        return nTotalSessNum;
    }    
    
    public int getConnectedNormalSessNum(String sNodeName, String sGroupCode) {
        return getConnectedSessNum(listWCALocalSessonPool, sNodeName, sGroupCode);
    }
    
    public int getConnectedBackupSessNum(String sNodeName, String sGroupCode) {
       return getConnectedSessNum(listWCABackupSessonPool, sNodeName, sGroupCode);
    }
    
    private int getConnectedSessNum(LinkedList sesspool) {
        WCASessionPool		wcaSessionPool = null;
        int					nTotalSessNum = 0;
        int                 nSessNum = 0;
        
        if (sesspool.size() != 0) {
            for (int i = 0; i < sesspool.size(); i++) {                
                wcaSessionPool = (WCASessionPool)sesspool.get(i);
                nSessNum = wcaSessionPool.getConnectedSessNum();
                nTotalSessNum += nSessNum;
            }               
        }
        
        return nTotalSessNum;
    }
    
    private int getConnectedSessNum(LinkedList sesspool, String sNodeName, String sGroupCode) {
        WCASessionPool		wcaSessionPool = null;
        int					nTotalSessNum = 0;
        int                 nSessNum = 0;
        
        if (sesspool.size() != 0) {
            for (int i = 0; i < sesspool.size(); i++) {                
                wcaSessionPool = (WCASessionPool)sesspool.get(i);
                nSessNum = wcaSessionPool.getConnectedSessNum();
                if (wcaSessionPool.getNodeName().equals(sNodeName)) {
                	if (wcaSessionPool.getGroupCode().equals(sGroupCode)) {
	                    nTotalSessNum += nSessNum;
                	}
                }
            }               
        }
        return nTotalSessNum;
    }
    
    /** 
    * 1. 기능 :  BACKUP POOL 수동 전환 모드
    * 2. 처리 개요 : 
    * 3. 주의사항
    * 
    * @param sNodeName	LU 그룹 노드 이름	
    * @param sGroupCode LU 그룹 이름
    * @param flag       Backup Mode (Y/N)
    **/
    public void setManualBackupMode(String sNodeName, String sGroupCode, boolean flag) throws WCAException {
    	// LU 그룹 노드 및 LU 그룹에 해당하는 WCA Session Pool을 구함 
    	WCASessionPool		wcaSessionPool = getLocalSessionPool(sNodeName, sGroupCode);
    	
    	// WCA Session Pool에 대한 Backup 전환 여부 가능 플래그 검사
    	if (wcaSessionPool.getBackupEnableFlag() == false) {
    		adapter_logger.warn("SNAAdapter:setManualBackupMode> [" + adapterGroupName + "] " + sNodeName + "-" + sGroupCode + ", Backup Enable Flag is False, So Return");
    		return;
    	}
    	
    	adapter_logger.warn("SNAAdapter:setManualBackupMode> [" + adapterGroupName + "] " + sNodeName + "-" + sGroupCode + ", Set Manual Backup Flag to " + flag);    	
        wcaSessionPool.setManualBackupMode(flag);
        
        // 백업 세션이 시작되지 않았으면 시작한다.
        if (flag) {
        	adapter_logger.warn("SNAAdapter:setManualBackupMode> [" + adapterGroupName + "] " + sNodeName + "-" + sGroupCode + ", Try to Start Backup Session Pool");
            startBackupSessionPool(sNodeName, sGroupCode);        	
        }
    }
    
    /** 
    * 1. 기능 :  BACKUP POOL 자동 전환 모드
    * 2. 처리 개요 : 
    * 3. 주의사항
    * 
    * @param sNodeName	LU 그룹 노드 이름
    * @param sGroupCode LU 그룹 이름
    * @param flag       Backup Mode (Y/N)
    **/
    public void setAutoBackupMode(String sNodeName, String sGroupCode, boolean flag) throws WCAException {
        WCASessionPool		wcaSessionPool = getLocalSessionPool(sNodeName, sGroupCode);
        
    	// WCA Session Pool에 대한 Backup 전환 여부 가능 플래그 검사
    	if (wcaSessionPool.getBackupEnableFlag() == false) {
   			adapter_logger.warn("SNAAdapter:setAutoBackupMode> [" + adapterGroupName + "] " + sNodeName + "-" + sGroupCode + ", Backup Enable Flag is False, So Return");
    		return;
    	}
    	
    	adapter_logger.warn("SNAAdapter:setAutoBackupMode> [" + adapterGroupName + "] " + sNodeName + "-" + sGroupCode + ", Set Auto Backup Flag to " + flag);     	
        wcaSessionPool.setAutoBackupMode(flag);
        
        // 백업 세션이 시작되지 않았으면 시작한다.
        if (flag) {
        	if (wcaSessionPool.getBackupCvsInfo().equalsIgnoreCase(WCADefine.WCA_AUTO_BACKUP_CVS_MODE_YES)) {        	
        		adapter_logger.warn("SNAAdapter:setAutoBackupMode> [" + adapterGroupName + "] " + sNodeName + "-" + sGroupCode + ", Try to Start Backup Session Pool");
        		startBackupSessionPool(sNodeName, sGroupCode);
        	}
        	else {
        		adapter_logger.warn("SNAAdapter:setAutoBackupMode> [" + adapterGroupName + "] " + sNodeName + "-" + sGroupCode + ", Auto Backup is False, So Return");        		
        	}
        }
    }
    
    public void setWCAAutoBackup(String sNodeName, String sGroupCode) throws WCAException {
        WCASessionPool		wcaSessionPool = getLocalSessionPool(sNodeName, sGroupCode);
        wcaSessionPool.setWCAAutoBackup();

        if (wcaSessionPool.getBackupCvsInfo().equalsIgnoreCase(WCADefine.WCA_AUTO_BACKUP_CVS_MODE_YES)) {        
        	startBackupSessionPool(sNodeName, sGroupCode);        
        }
        else {
    		adapter_logger.warn("SNAAdapter:setWCAAutoBackup> [" + adapterGroupName + "] " + sNodeName + "-" + sGroupCode + ", Auto Backup is False, So Return");        	
        }
    }
    
    /** 
    * 1. 기능 :  현재 설정된 backup 모드 반환
    * 2. 처리 개요 : 
    * 3. 주의사항
    * 
    * @param 	sNodeName		LU 그룹이 속한 노드 이름
    * @param    sGroupCode      LU 그룹 이름
    * @return   Backup Mode (Y/N)
    **/
    public boolean getBackupMode(String sNodeName, String sGroupCode) throws WCAException {
        WCASessionPool		wcaSessionPool = getLocalSessionPool(sNodeName, sGroupCode);
        return wcaSessionPool.getBackupMode();
    }
    

    /** 
    * 1. 기능 :  어댑터 그룹, LU 그룹 별 백업 세션 반환
    * 2. 처리 개요 : 
    * 3. 주의사항
    * 
    * @param sAdapterGroupName 어댑터 그룹 이름
    * @param sNodeName LU 그룹 노드 이름
    * @param sGroupCode LU 그룹 이름
    * 
    * @return WCASession 백업 세션
    **/
    public WCASession getBackupWCASessionInSessionPoolList(String sNodeName, String sGroupCode) {
        WCASessionPool		wcaSessionPool = null;
        WCASession			wcaSession = null;        
   
        if (adapter_logger.isDebug()) {
        	adapter_logger.debug("SNAAdapter:getBackupWCASessionInSessionPoolList> [" + adapterGroupName + "] " + "Try to Find Session in Backup Session Pool List, NodeName = " + sNodeName + ", LU Group Name = " + sGroupCode);
        }
        
        if (listWCABackupSessonPool.size() != 0) {
            for (int i = 0; i < listWCABackupSessonPool.size(); i++) {
                wcaSessionPool = (WCASessionPool)listWCABackupSessonPool.get(i);
                if (wcaSessionPool.getNodeName().equals(sNodeName) && wcaSessionPool.getGroupCode().equals(sGroupCode)) {
                	// WCA 세션풀내에서 이용가능한 세션을 부하 분산울 위해서 라운드 로빈 방식으로 구한다.
                    wcaSession = wcaSessionPool.getWCASessionInSessionPool();
                    break;
                }
            }
        }
        else {
        	adapter_logger.warn("SNAAdapter:getBackupWCASessionInSessionPoolList> [" + adapterGroupName + "] " + "listWCABackupSessonPool Size is 0, NodeName = " + sNodeName + ", LU Group Name = " + sGroupCode);        	
        }
        
        return wcaSession;
    }
    
   /** 
    * 1. 기능 :  어댑터 그룹, LU 그룹 별 세션 반환
    * 2. 처리 개요 : 
    * 3. 주의사항
    * 
    * @param    sGroupCode      LU 그룹 이름
    * @return   WCASession 세션
    **/
    public WCASession getWCASessionInSessionPoolList() throws WCAException {
        int					i = 0;
        WCASessionPool		wcaSessionPool = null;
        WCASession			wcaSession = null;
        
        if (adapter_logger.isDebug()) {
        	adapter_logger.debug("SNAAdapter:getWCASessionInSessionPoolList> [" + adapterGroupName + "] " + "Try to Find Session in Session Pool List");
        }
        
        // 세션 그룹이 없는 경우
        if (listWCALocalSessonPool.size() == 0){
        	adapter_logger.warn("SNAAdapter:getWCASessionInSessionPoolList> [" + adapterGroupName + "] " + "listWCALocalSessonPool Size is 0");
            throw new WCAException( WCAException.E_SOFTLINK_NOT_FOUND_SESSION, adapterGroupName);
        }
        
        // 부하 분산을 위해서 WCA 세션 풀간에서 이용가능한 WCA 세션을 구함 
        nLocalSessionPoolIdx = nLocalSessionPoolIdx % listWCALocalSessonPool.size();
        for (i = nLocalSessionPoolIdx; i < listWCALocalSessonPool.size(); i++) {
            wcaSessionPool = (WCASessionPool)listWCALocalSessonPool.get(i);
                           
            if (wcaSessionPool.getBackupMode()) {
                wcaSession = getBackupWCASessionInSessionPoolList(wcaSessionPool.getNodeName(), wcaSessionPool.getGroupCode());
            }
            else {
            	// WCA 세션풀내에서 이용가능한 세션을 부하 분산울 위해서 라운드 로빈 방식으로 구한다.
                wcaSession = wcaSessionPool.getWCASessionInSessionPool();            	
            }

            // 이용가능한 WCA 세션을 찾은 경우, Loop Break
            if (wcaSession != null) {
                break;
            }
        }
        
        // 처음부터 다시 검색
        if (wcaSession == null) {
            for (i = 0; i < nLocalSessionPoolIdx; i++) {
                wcaSessionPool = (WCASessionPool)listWCALocalSessonPool.get(i);
                
                if (wcaSessionPool.getBackupMode()) {
                    wcaSession = getBackupWCASessionInSessionPoolList(wcaSessionPool.getNodeName(), wcaSessionPool.getGroupCode());
                }
                else {
                	// WCA 세션풀내에서 이용가능한 세션을 부하 분산울 위해서 라운드 로빈 방식으로 구한다.
                    wcaSession = wcaSessionPool.getWCASessionInSessionPool();
                }
                
                // 이용가능한 WCA 세션을 찾은 경우, Loop Break                
                if (wcaSession != null) {
                    break;              
                }
            }
        }
        
        if (wcaSession != null) {
            nLocalSessionPoolIdx++;
            return wcaSession;
        }
        
        nLocalSessionPoolIdx++;
        
        adapter_logger.warn("SNAAdapter:getWCASessionInSessionPoolList> [" + adapterGroupName + "] " + "Can't Find Available WCA Session in Session Pool List");        
        throw new WCAException(WCAException.E_SOFTLINK_NO_SESSION_SLOT, toString());
    }
    
   /** 
    * 1. 기능 :  LU 그룹 노드 및 LU 그룹에 해당하는 이용가능한 세션 반환
    * 2. 처리 개요 : 
    * 		- 세션 풀 모드가 백업 일경우 백업 세션에서 할당받는다.
    * 3. 주의사항
    * 
    * @param 	sNodeName		LU 그룹 노드 이름
    * @param    sGroupCode      LU 그룹 이름
    * 
    * @return   WCASession 세션 오브젝트
    **/
   
    public WCASession getWCASessionInSessionPoolList(String sNodeName, String sGroupCode) throws WCAException {
        int					i               = 0;
        WCASessionPool		wcaSessionPool  = null;
        WCASession			wcaSession      = null;
        
        if (adapter_logger.isDebug()) {
        	adapter_logger.debug("SNAAdapter:getWCASessionInSessionPoolList> [" + adapterGroupName + "] " + "Try to Find Session in Session Pool List, NodeName = " + sNodeName + ", LU Group Name = " + sGroupCode);
        }
        
        if (listWCALocalSessonPool.size() == 0) {
        	adapter_logger.warn("SNAAdapter:getWCASessionInSessionPoolList> [" + adapterGroupName + "] " + "listWCALocalSessonPool Size is 0, NodeName = " + sNodeName + ", LU Group Name = " + sGroupCode);            
            throw new WCAException( WCAException.E_SOFTLINK_NOT_FOUND_SESSION, adapterGroupName );
        }
        
        for (i = 0; i < listWCALocalSessonPool.size(); i++) {
            wcaSessionPool = (WCASessionPool)listWCALocalSessonPool.get(i);
                       
            // WCA 세션 Pool의 노드 이름 비교
            if (!wcaSessionPool.getNodeName().equals(sNodeName)) {
            	continue;
            }
            
            // WCA 세션 Pool의 LU 그룹 이름 비교            
            if(!wcaSessionPool.getGroupCode().equals(sGroupCode)) { 
            	continue;
            }
            
            // 백업 모드인 경우 
            if (wcaSessionPool.getBackupMode()) {
                wcaSession = getBackupWCASessionInSessionPoolList(wcaSessionPool.getNodeName(), wcaSessionPool.getGroupCode());
            }
            else {
            	// WCA 세션풀내에서 이용가능한 세션을 부하 분산울 위해서 라운드 로빈 방식으로 구한다.
                wcaSession = wcaSessionPool.getWCASessionInSessionPool();            	
            }
            
            // if(wcaSession != null)
            break;
        }       
                
        if (wcaSession != null) {
            return wcaSession;
        }
            
        adapter_logger.warn("SNAAdapter:getWCASessionInSessionPoolList> [" + adapterGroupName + "] " + "Can't Find Available WCA Session, NodeName = " + sNodeName + ", LU Group Name = " + sGroupCode);        
        throw new WCAException( WCAException.E_SOFTLINK_NO_SESSION_SLOT, adapterGroupName+"/"+ sNodeName + "/" + sGroupCode); 
    }
    
   
    /** 
     * 1. 기능 :  노드 이름 및 LU 그룹에 해당하는 백업 세션 풀 반환
     * 2. 처리 개요 : 
     * 3. 주의사항
     * 
     * @param    sGroupCode      LU 그룹 이름
     * @return   백업 WCASessionPool
     **/
    public WCASessionPool getBackupSessionPool(String sNodeName, String sGroupCode)  throws WCAException {
    	WCASessionPool		wcaSessionPool = null;
         
        if (listWCABackupSessonPool.size() != 0) {
            for (int i = 0; i < listWCABackupSessonPool.size(); i++) {
                wcaSessionPool = (WCASessionPool)listWCABackupSessonPool.get(i);
                if (wcaSessionPool.getNodeName().equals(sNodeName)) {
                	if (wcaSessionPool.getGroupCode().equals(sGroupCode)) {
                 		return wcaSessionPool; 
                 	}
                }
            }
        }
        
        throw new WCAException(WCAException.E_SOFTLINK_NOT_FOUND_SESSION, adapterGroupName+ "/" + sNodeName + "/" + sGroupCode + " 백업 세션" );
     }  
     
    public WCASessionPool getLocalSessionPool(String sNodeName, String sGroupCode)  throws WCAException {
        WCASessionPool		wcaSessionPool = null;
        
        if (listWCALocalSessonPool.size() != 0) {
            for (int i = 0; i < listWCALocalSessonPool.size(); i++) {
                wcaSessionPool = (WCASessionPool)listWCALocalSessonPool.get(i);
                if (wcaSessionPool.getNodeName().equals(sNodeName)) {
                	if (wcaSessionPool.getGroupCode().equals(sGroupCode)) {
                		return wcaSessionPool; 
                	}
                }
            }
        }
        
        throw new WCAException(WCAException.E_SOFTLINK_NOT_FOUND_SESSION, adapterGroupName + "/" + sNodeName + "/" + sGroupCode + " 정상 세션" );
    }
    
    
    public void printSessionPool() {
        int					i = 0;
        WCASessionPool		wcaSessionPool = null;
    
        // 정상 세션 내용 로깅
        if (listWCALocalSessonPool.size() != 0) {
            for (i = 0; i < listWCALocalSessonPool.size(); i++) {
                wcaSessionPool = (WCASessionPool)listWCALocalSessonPool.get(i);                
                adapter_logger.debug("\t " +wcaSessionPool.toString());
            }
        }
        
        // 백업 세션 내용 로깅
        if (this.listWCABackupSessonPool.size() != 0) {
            for (i = 0; i < listWCABackupSessonPool.size(); i++) {
                wcaSessionPool = (WCASessionPool)listWCABackupSessonPool.get(i);                
                adapter_logger.debug("\t" + wcaSessionPool.toString());
            }
        }
    }
       
    // 20090228 Start
    public String getStartBackupSessionProperty(String adapterName) {
        String 				sStartBackupSession = null;
        String				propertyName = "START_BACKUP_SESSION";
        
       	adapter_logger.warn("SNAAdapter:getStartBackupSessionProperty> PROPERTY_GROUP = " + adapterName + ", PROPERTY = " + propertyName);
        
        sStartBackupSession = AdapterPropManager.getInstance().getProperty(adapterName, propertyName);
        
        // Property가 설정되어 있지 않은 경우, 디폴트 값인 "0" 사용
        if (sStartBackupSession == null) {
   			adapter_logger.warn("SNAAdapter:getStartBackupSessionProperty> [" + adapterName + "] sStartBackupSession is NULL, So Set To 0");
       		sStartBackupSession = "0";
        }
        
        // Property 값이 유효하지 않은 경우, 디폴트 값인 "0" 사용
        if (sStartBackupSession.trim().length() == 0) {
   			adapter_logger.warn("SNAAdapter:getStartBackupSessionProperty> [" + adapterName + "] sStartBackupSession Length is 0, , So Set To 0");        	
      		sStartBackupSession = "0";
        }
              
        adapter_logger.warn("SNAAdapter:getStartBackupSessionProperty> [" + adapterName + "] sStartBackupSession = " + sStartBackupSession);
        
        return sStartBackupSession;
    }
    
    public int getLuGroupMapCount() {
    	return lugrpCount;
	}
    
    private int getLuGrpNodeAndGroupMapCount() {
    	int		nTempLuGrpCount = 0;
    	
    	if (lugrpNodeMap == null) {
    		adapter_logger.warn("SNAAdapter:getLuGrpMapCount> [" + adapterGroupName + "] " + "lugrpNodeMap is Null");
    		return 0;    		
    	}
    	
    	Iterator iter = lugrpNodeMap.keySet().iterator();
    	while (iter.hasNext()) {
    		String sNodeName = (String)iter.next();
    		HashMap lugrpGroupMap = (HashMap)lugrpNodeMap.get(sNodeName);
    		if (lugrpGroupMap == null) {
    			continue;
    		}
    		
    		nTempLuGrpCount += lugrpGroupMap.keySet().size();
    	}
    	
    	return nTempLuGrpCount;
    }
    
   
    public SessionGroupDataEx getLuGroupInfo(HashMap lugrpNodeMap, String sNodeName, String sLuGroupName) {
        // 3. 노드 이름에 해당하는 LU 그룹 맵이 LU 그룹 노드맵에 존재하는지 검사
        HashMap lugrpGroupMap = (HashMap)lugrpNodeMap.get(sNodeName);
        if (lugrpGroupMap == null) {
        	adapter_logger.warn("SNAAdapter:getLuGroupInfo> [" + adapterGroupName + "] " + "lugrpGroupMap is Null, NodeName = " + sNodeName);        	
            return null;
        }             

        // 4. LU 그룹에 해당하는 LU 그룹 정보가 LU 그룹 맵에 존재하는지 검사
        SessionGroupDataEx lugrpInfo = (SessionGroupDataEx)lugrpGroupMap.get(sLuGroupName);
        if (lugrpInfo == null) {
        	adapter_logger.warn("SNAAdapter:getLuGroupInfo> [" + adapterGroupName + "] " + "lugrpInfo is Null, NodeName = " + sNodeName + ", LU Group Name = " + sLuGroupName);        	
            return null;
        }    	
        else {
        	return lugrpInfo;
        }
    }
    
    /** 
     * 1. 기능 :  LU 그룹 세션 정보 추가
     * 2. 처리 개요 : 
     *       - LU 그룹 세션 정보를 메모리에 추가한다.
     *       - LU 그룹 세션 구동한다.
     * 3. 주의사항
     * 
     * @param    SessionGroupData     LU 그룹 세션 정보
     **/
    public void insertLuGroupInfoEx(SessionGroupDataEx data) throws WCAException {
    	String		sLogStr = "";
    	
    	try {
    		adapter_logger.warn("SNAAdapter:insertLuGroupInfoEx> Start Insert LU Group Info, Session Group Info = " + data.toString());
                       
    		String 		sHostName = data.getNodeName();
    		String		sLuGroupName = data.getAdapterGroupName();
    		sLogStr =  "[" + adapterGroupName + "][" + sHostName + "][" + sLuGroupName + "]";
    		
    		// LU 그룹 노드 정보는 어댑터가 기동시에 WCA (SOFTLINK_IP) 프라퍼티를 통해서 설정됨
    		// 어댑터가 연결하고 있는 LU 그룹 노드 정보와 비교하여 존재하지 않으면,
    		// 자신과 관련 없는 노드에 LU 그룹이 추가된 것이기 때문에 데이터를 무시함.
    		HashMap 	lugrpGroupMap = (HashMap)lugrpNodeMap.get(sHostName);
    		if (lugrpGroupMap == null) {
    			adapter_logger.warn("SNAAdapter:insertLuGroupInfoEx> " + sLogStr + " There is No Match LU Group Node Map");
    			return;
    		}

    		// LU 그룹 정보에 대한 Softlink IP 주소, 백업 세션 풀 시작 옵션, 백업 세션 사용 가능 여부 플래등을 설정    		
    		if (setLuGroupAdditionalInfo(data) == false) {
    			adapter_logger.warn("SNAAdapter:insertSessionGroupEx> " + sLogStr + " setLuGroupAddtionalInfo() Fail");
    			return;
    		}

            adapter_logger.warn("SNAAdapter:insertSessionGroupEx> " + sLogStr + " Put Adapter LU Session Group to LU Group Map");
    		lugrpGroupMap.put(sLuGroupName, data);
    		lugrpCount++;

            adapter_logger.warn("SNAAdapter:insertSessionGroupEx> " + sLogStr + " Init And Start Session Pool for LU Group");    		
            initAndStartLocalAndBackupSessionPool(data);	
    		
    		adapter_logger.warn("SNAAdapter:insertSessionGroupEx> " + sLogStr + " Complete Insert LU Group");    		
        }
        catch (Exception e) {
        	adapter_logger.error("SNAAdapter:insertSessionGroupEx> " + sLogStr, e);
        }
    }
    
    /** 
     * 1. 기능 :  LU 그룹 세션 정보 삭제
     * 2. 처리 개요 : 
     *       - LU 그룹 세션 종료한다.
     *       - LU 그룹 세션 정보를 메모리에 삭제한다.
     * 3. 주의사항
     * 
     * @param    SessionGroupData     LU 그룹 세션 정보
     **/
    public void deleteLuGroupInfoEx(SessionGroupDataEx data) {
    	String sLogStr = "";
    	
    	try {
    		adapter_logger.warn("SNAAdapter:deleteLuGroupInfoEx> Start To Delete LU Group Info, Session Group Info = " + data.toString());
    		
    		String 		sHostName = data.getNodeName();
    		String		sLuGroupName = data.getAdapterGroupName();
    		sLogStr =  "[" + adapterGroupName + "][" + sHostName + "][" + sLuGroupName + "]";
    	
    		// LU 그룹 노드 정보는 어댑터가 기동시에 WCA (SOFTLINK_IP) 프라퍼티를 통해서 설정됨
    		// 어댑터가 연결하고 있는 LU 그룹 노드 정보와 비교하여 존재하지 않으면,
    		// 자신과 관련 없는 노드에 LU 그룹이 추가된 것이기 때문에 데이터를 무시함.
    		HashMap 	lugrpGroupMap = (HashMap)lugrpNodeMap.get(sHostName);
    		if (lugrpGroupMap == null) {
    			adapter_logger.warn("SNAAdapter:deleteLuGroupInfoEx> " + sLogStr + " There is No Match LU Group Node Map");
    			return;
    		}
    		
            stopSessionPoolEx(data);
             
            adapter_logger.warn("SNAAdapter:deleteLuGroupInfoEx> " + sLogStr + " Remove Adapter LU Session Group to LU Group Map");
            lugrpGroupMap.remove(sLuGroupName);
            lugrpCount--;
            
    		adapter_logger.warn("SNAAdapter:deleteLuGroupInfoEx> " + sLogStr + " Complete Delete LU Group");            
        }
        catch (Exception e) {
        	adapter_logger.error("SNAAdapter:deleteLuGroupInfoEx> " + sLogStr + " Exception Msg = " + e.getMessage(), e);            
        }
    }

    /** 
     * 1. 기능 :  LU 세션 풀, 백업 세션 풀 갱신
     * 2. 처리 개요 : 
     *     - ip, port가 변하지 않고 wca 세션수만 증가했다면, 증가한 세션 수 만큼 증가시킨다.
     *     - 세션 정보를 갱신한다.
     * 3. 주의사항
     * @param  sessgroup SessionGroupData object
     **/
    public void updateLuGroupInfoEx(SessionGroupDataEx data) throws WCAException {
    	String sLogStr = "";
    	
    	try {
    		adapter_logger.warn("SNAAdapter:updateLuGroupInfoEx> Start To Update LU Group Info, Session Group Info = " + data.toString());
    		
    		String 		sHostName = data.getNodeName();
    		String		sLuGroupName = data.getAdapterGroupName();
    		
    		// 로그용 정보 문자열 조립
    		sLogStr =  "[" + adapterGroupName + "][" + sHostName + "][" + sLuGroupName + "]";
    	
    		// LU 그룹 노드 정보는 어댑터가 기동시에 WCA (SOFTLINK_IP) 프라퍼티를 통해서 설정됨
    		// 어댑터가 연결하고 있는 LU 그룹 노드 정보와 비교하여 존재하지 않으면,
    		// 자신과 관련 없는 노드에 LU 그룹이 추가된 것이기 때문에 데이터를 무시함.
    		HashMap 	lugrpGroupMap = (HashMap)lugrpNodeMap.get(sHostName);
    		if (lugrpGroupMap == null) {
    			adapter_logger.warn("SNAAdapter:updateLuGroupInfoEx> " + sLogStr + " There is No Match LU Group Node Map");
    			return;
    		}
    		
    		// LU 그룹 정보에 대한 Softlink IP 주소, 백업 세션 풀 시작 옵션, 백업 세션 사용 가능 여부 플래등을 설정 
    		if (setLuGroupAdditionalInfo(data) == false) {
    			adapter_logger.warn("SNAAdapter:updateLuGroupInfoEx> " + sLogStr + " setLuGroupAddtionalInfo() Fail");
    			return;
    		}

    		WCASessionPool	wcaSessionPool = null;
    		WCASessionPool	wcaBackupSessionPool = null;
         
    		// WCA 세션 Pool을 찾음
    		wcaSessionPool = getLocalSessionPool(sHostName, sLuGroupName);
    		if (wcaSessionPool == null) {
    			adapter_logger.warn("SNAAdapter:updateLuGroupInfoEx> " + sLogStr + " Can't Find Local Session Pool");    			
    			return;
    		}
    		
    		wcaSessionPool.updateLuGroupInfoEx(data);
    		
    		// 세션 수가 증가된 경우에는 WCA 세션 수 증가
    		increaseSessionPoolEx(wcaSessionPool, data);

    		// 백업 세션이 정의되지 않았으면 return
    		if (data.getBackupNodeName().equals("") || data.getBackupPort() == 0 || data.getSessionCnt() == 0)
    			return;
         
    		try {
    			wcaBackupSessionPool = getBackupSessionPool(sHostName, sLuGroupName);
    			wcaBackupSessionPool.updateLuGroupInfoEx(data);             
    			increaseSessionPoolEx(wcaBackupSessionPool, data);
    		}
    		catch (WCAException e) {
    			// 백업 세션이 없을 경우 백업 세션 생성
    			initSessionPoolInfo(data, false);
    		}
    		
    	    if (data.getStartBackupSession().equals("1")) {        	
    	       	adapter_logger.warn("SNAAdapter:updateLuGroupInfoEx> " + sLogStr + " Start Backup Session Pool");
    	     	startSessionPool(false);
    	    }                    	
    	    else {
    	     	adapter_logger.warn("SNAAdapter:updateLuGroupInfoEx> " + sLogStr + " Don't Start Backup Session Pool");                    	
    	    }
    	}
        catch (Exception e) {
        	adapter_logger.error("SNAAdapter:updateLuGroupInfoEx> " + sLogStr, e);            
        }    	
    }
     
    /** 
     * 1. 기능 : WCA APP CODE 정보 갱신 (updateSessionInfoEx 함수를 참조하여 개발)
     * 2. 처리 개요 : 
     * 3. 주의사항 
     * 
     * @param  data SessionGroupDataEx object 
     **/ 
    public void updateWcaAppCodeEx(WcaInfoDataEx wcaInfo) throws WCAException {
    	// 0. 새롭게 LU 그룹 정보 맵을 로딩하고, 기존 어댑터 그룹에 해당하는 LU 그룹 노드 맵을 구함
        HashMap luGrpNodeMap = loadAdapterInfo(adapterGroupName, adapterName);
    	
    	if (luGrpNodeMap == null) {
    		throw new WCAException("SNAAdapter:updateWcaAppCodeEx> luGrpNodeMap not found adapterGroupName=" + adapterGroupName + ", adapterName=" + adapterName);
    	}
    	
    	try {
        	AdapterPropManager.getInstance().setProperty(adapterName, "APP_CODE", wcaInfo.appCd);
        	adapter_logger.error("SNAAdapter:updateWcaAppCodeEx> [" + adapterGroupName + "][" + adapterName  + "] Success to set Property APP_CODE = [" + wcaInfo.appCd + "]");
        	
	        // 기존에도 있어고 현재에도 있는 것들은 정보가 갱신된 경우 처리
	        Iterator iterNewNode = luGrpNodeMap.keySet().iterator();
	        while (iterNewNode.hasNext()) {
	        	String 		sNodeName = (String)iterNewNode.next();       	
	        	HashMap 	luGrpGroupMap = (HashMap)luGrpNodeMap.get(sNodeName);
	        	Iterator 	iterNewGroup = luGrpGroupMap.keySet().iterator();
	        	
	        	while (iterNewGroup.hasNext()) {
	        		String sLuGroupName = (String)iterNewGroup.next();
	        		SessionGroupDataEx oNewLuGroupInfo = (SessionGroupDataEx)luGrpGroupMap.get(sLuGroupName);
	        		
	        		// 기존 LU 그룹 정보에서 갱신된 LU 그룹 정보에 해당하는 것을 찾지 못한 경우, Continue
	        		SessionGroupDataEx oLuGroupInfo = getLuGroupInfo(lugrpNodeMap, oNewLuGroupInfo.getNodeName(), oNewLuGroupInfo.getGroupName());
	        		if (oLuGroupInfo == null) { continue; }

	        		adapter_logger.warn("SNAAdapter:updateWcaAppCodeEx> [" + adapterGroupName + "] Update WCA App Code = [" + oLuGroupInfo.getNodeName() + "][" + oLuGroupInfo.getGroupName() + "]");
	        		
	        		WCASessionPool wcaLocalSessionPool = null;
	        		WCASessionPool wcaBackupSessionPool = null;
	        		
	        		// 로컬 세션 풀 정보 갱신
	        		wcaLocalSessionPool = getLocalSessionPool(oLuGroupInfo.getNodeName(), oLuGroupInfo.getGroupName());
	        		if (wcaLocalSessionPool != null) {
	        			adapter_logger.warn("SNAAdapter:updateWcaAppCodeEx> [" + adapterGroupName + "] wcaLocalSessionPool is not null");
	        			wcaLocalSessionPool.updateWcaAppCodeEx();
	        		}
	        		else {
		        		adapter_logger.warn("SNAAdapter:updateWcaAppCodeEx> [" + adapterGroupName + "] wcaLocalSessionPool is null");	        			
	        		}
	        		
	        		// 백업 세션 풀 정보 갱신
	        		wcaBackupSessionPool = getBackupSessionPool(oLuGroupInfo.getNodeName(), oLuGroupInfo.getGroupName());
	        		if (wcaBackupSessionPool != null) {
	        			adapter_logger.warn("SNAAdapter:updateWcaAppCodeEx> [" + adapterGroupName + "] wcaBackupSessionPool is not null");
	        			wcaBackupSessionPool.updateWcaAppCodeEx();	        			
	        		}
	        		else {
		        		adapter_logger.warn("SNAAdapter:updateWcaAppCodeEx> [" + adapterGroupName + "] wcaBackupSessionPool is null");	        			
	        		}	        		
	        	}        	
	        }
    	}
    	catch (Exception e) {
    		adapter_logger.warn("SNAAdapter:updateWcaAppCodeEx> [" + adapterGroupName + "] Update LU Group Exception, Msg = " + e.getMessage(), e);
    	}
    }
     
    public void printThreadInfo() {        
        if (threadNodeMap == null) {
        	adapter_logger.warn("SNAAdapter:printThreadInfo> [" + adapterGroupName + "] threadNodeMap is NULL, So Return");              	
        	return;
        }
        
        Iterator iterNode = threadNodeMap.keySet().iterator();
        while (iterNode.hasNext()) {
            String sNodeName = (String)iterNode.next();
            printThreadInfo(sNodeName);            
        }
    }
     
     public void printThreadInfo(String sNodeName) {        
         if (threadNodeMap == null) {
         	adapter_logger.warn("SNAAdapter:printThreadInfo> [" + adapterGroupName + "] threadNodeMap is NULL, So Return");              	
         	return;
         }
         
         HashMap threadGroupMap = (HashMap)threadNodeMap.get(sNodeName);
         if (threadGroupMap == null) {
        	 return;
         }
                     
         Iterator iterGroup = threadGroupMap.keySet().iterator();
         while (iterGroup.hasNext()) {
         	String sLuGroupName = (String)iterGroup.next();
         	printThreadInfo(sNodeName, sLuGroupName);                
         }
     }
     
     public void printThreadInfo(String sNodeName, String sLuGroupName) {           
         if (threadNodeMap == null) {
         	adapter_logger.warn("SNAAdapter:printThreadInfo> [" + adapterGroupName + "] threadNodeMap is NULL, So Return");              	
         	return;
         }
         
         HashMap threadGroupMap = (HashMap)threadNodeMap.get(sNodeName);
         if (threadGroupMap == null) { 
        	 return;
         }
         
         ThreadGroup info = (ThreadGroup)threadGroupMap.get(sLuGroupName);   
         if (info == null) {
        	 return;
         }
             
       	 adapter_logger.warn("SNAAdapter:printThreadInfo> [" + adapterGroupName + "]" + info.toString());
         
         return;
    }       

    /**
     * LU 그룹 정보에 대한 Softlink IP 주소와 백업 세션 풀 시작 옵션, 백업 세션 사용 가능 여부 플래등을 설정
     * 
     * @param 	data LU 그룹에 대한 세션 정보
     * 
     * @return	LU 그룹에 대한 세션 정보 갱신 성공 여부
     */
    public boolean setLuGroupAdditionalInfo(SessionGroupDataEx data) {
		String softlinkIpAddr = "127.0.0.1";
		String sStartBackupSession = "0";
		
		try {
	        String localHostName = EAIServerManager.getInstance().getLocalServerHostName();
	        if (localHostName.equals(data.getNodeName())) {
	        	adapter_logger.warn("SNAAdapter:setLuGroupAddtionalInfo> LU Group Node Name equals Local Host Name, So Set Softlink IP to 127.0.0.1");            	
	        	softlinkIpAddr = "127.0.0.1";
	        }
	        else {
	    		// LU 호스트 테이블에서 추가된 LU 그룹의 노드 이름에 해당하는 IP 주소를 가져옴   
	        	SessionGroupJDBCEx	jdbc = new SessionGroupJDBCEx();
	        	softlinkIpAddr = jdbc.getSoftlinkIpAddr(data.getNodeName());
	        	if (softlinkIpAddr == null) {
	        		adapter_logger.error("SNAAdapter:setLuGroupAddtionalInfo> softlinkIpAddr is NULL, NodeName = " + data.getNodeName());	        		
	        		return false;
	        	}
	        	
	        	softlinkIpAddr = softlinkIpAddr.trim();
	        	
	        	if (softlinkIpAddr.length() == 0) {
	        		adapter_logger.error("SNAAdapter:setLuGroupAddtionalInfo> softlinkIpAddr Length is 0, NodeName = " + data.getNodeName());	        		
	        		return false;	        	
	        	}
	        }
	        	
	        adapter_logger.warn("SNAAdapter:setLuGroupAddtionalInfo> softlinkIpAddr = " + softlinkIpAddr + ", softlinkHostName = " + data.getNodeName());
	        
	        String softlinkIpAddrs = getSoftlinkIpAddrProperty(adapterName);
	        ArrayList alSoftlinkIpAddr = MyUtil.stringTokenizer(softlinkIpAddrs, ",");
	        
	        if (alSoftlinkIpAddr == null) {
	        	throw new Exception("softlinkIpAddrs is NULL");
	        }
	        
	        if (alSoftlinkIpAddr.size() > 1) {
	        	adapter_logger.warn("SNAAdapter:setLuGroupAddtionalInfo> [" + adapterGroupName + "] Set Forcely sStartBackupSessionFlag = 0");
	        	data.setBackupEnableFlag(false);
	       	 	sStartBackupSession = "0";            	
	        }
	        else {
	        	data.setBackupEnableFlag(true);        	
	        	// 백업 세션 시작 여부 정보를 Property에서 읽어옴
	        	sStartBackupSession = getStartBackupSessionProperty(adapterName);            	
	        }
	
	    	adapter_logger.warn("SNAAdapter:setLuGroupAddtionalInfo> [" + adapterGroupName + "] sStartBackupSessionFlag = " + sStartBackupSession);        
	        data.setStartBackupSession(sStartBackupSession);
	                
	        return true;
		}
        catch (Exception e) {
      	  	adapter_logger.warn("SNAAdapter:setLuGroupAddtionalInfo> ", e);            
            return false;
        }		
    }
    

    
    /** 
     * 1. 기능 :  HashMap에 저장된 어댑터 그룹, LU 그룹에 속한 Thread Group 정보 반환
     * 2. 처리 개요 : 
     * 3. 주의사항
     * 
     * @param    sAdapterGrpName     어댑터 그룹 이름
     * @param    sGroupCode          LU 그룹 이름
     * @return   Thread Group 정보 정보들 (ThreadGroup Object를 저장한 HashMap)
     **/
    public ThreadGroup getThreadGroup(String sNodeName, String sLuGroupName) {
         // LU 세션 그룹
         HashMap threadGroupMap = (HashMap)threadNodeMap.get(sNodeName);
         if (threadGroupMap == null) {
        	 adapter_logger.warn("SNAAdapter:getThreadGroup> [" + adapterGroupName + "] threadGroupMap is NULL, NodeName = " + sNodeName);           	 
             return null;
         }
         
         ThreadGroup threadGroup = (ThreadGroup)threadGroupMap.get(sLuGroupName);
         return threadGroup;
     }     
     
    /** 
     * 1. 기능 :  Thread Group 생성 및 HashMap에 저장
     * 2. 처리 개요 : 
     * 3. 주의사항
     * 
     * @param	nodeName			SoftLink 노드 이름
     * @param   lugrpName          LU 그룹 이름
     * @param   threadGroupName    Thread Group 이름
     * 
     * @return  Thread Group 정보 오브젝트 (ThreadGroup Object를 저장한 HashMap)
     **/
    public synchronized ThreadGroup putThreadGroup(String nodeName, String lugrpName, String threadGroupName) {
    	ThreadGroup		threadGroup = null;
                  
    	adapter_logger.warn("SNAAdapter:putThreadGroup> [" + adapterGroupName + "] Start To PU Thread Group, NodeName = " + nodeName + ", LU Group Name = " + lugrpName);
    	
        // 어댑터 그룹에 해당하는 Softlink 노드별 쓰레드 그룹 맵이 없을 경우, 새로운 맵을 생성하고 threadGroups 맵에 추가
        if (threadNodeMap == null) {
        	adapter_logger.warn("SNAAdapter:putThreadGroup> [" + adapterGroupName + "] threadNodeMap is NULL, So Create New One");        	
        	threadNodeMap = new HashMap();
        }
         
        // Softlink 노드별 쓰레드 그룹 맵에서 LU 그룹별 맵을 얻음 
        HashMap 		threadGroupMap = (HashMap)threadNodeMap.get(nodeName);
         
        // Softlink 노드별 쓰레드 그룹 맵에서 LU 그룹별 맵이 없을 경우, 새로운 맵을 생성하고 을 얻음
        if (threadGroupMap == null) {
        	adapter_logger.warn("SNAAdapter:putThreadGroup> [" + adapterGroupName + "] threadGroupMap is NULL, So Create New One, NodeName = " + nodeName);        	
        	threadGroupMap = new HashMap();
             
        	adapter_logger.warn("SNAAdapter:putThreadGroup> [" + adapterGroupName + "] Create New Thread Group, Thread Group Name = " + threadGroupName);        	
            threadGroup = new ThreadGroup(threadGroupName);
            
            adapter_logger.warn("SNAAdapter:putThreadGroup> [" + adapterGroupName + "] Put Thread Group to Thread Group Map, LU Group Name = " + lugrpName + ", Thread Group Name = " + threadGroupName);            
            threadGroupMap.put(lugrpName, threadGroup);
            
            adapter_logger.warn("SNAAdapter:putThreadGroup> [" + adapterGroupName + "] Put Thread Group Map to Thread Node Map, LU Group Name = " + lugrpName + ", Thread Group Name = " + threadGroupName);            
            synchronized(threadNodeMap) {
            	threadNodeMap.put(nodeName, threadGroupMap);
            }
        }
        else {
        	adapter_logger.warn("SNAAdapter:putThreadGroup> [" + adapterGroupName + "] Thread Group Map Already Exist, LU Group Name = " + lugrpName);        	
            threadGroup = (ThreadGroup)threadGroupMap.get(lugrpName);
            if (threadGroup == null) {
            	adapter_logger.warn("SNAAdapter:putThreadGroup> [" + adapterGroupName + "] Create New Thread Group, Thread Group Name = " + threadGroupName);            	
            	threadGroup = new ThreadGroup(threadGroupName);

                adapter_logger.warn("SNAAdapter:putThreadGroup> [" + adapterGroupName + "] Put Thread Group to Thread Group Map, LU Group Name = " + lugrpName + ", Thread Group Name = " + threadGroupName);            	
                threadGroupMap.put(lugrpName, threadGroup);                
            }
            else {
            	adapter_logger.warn("SNAAdapter:putThreadGroup> [" + adapterGroupName + "] Thread Group Already Exist, LU Group Name = " + lugrpName);            	
            }
            
        }
        
        return threadGroup;
    }   

    /** 
     * 1. 기능 :  Thread Group 삭제
     * 2. 처리 개요 : 
     * 3. 주의사항
     * 
     * @param    sAdapterGrpName     어댑터 그룹 이름
     * @param    sGroupCode          LU 그룹 이름
     **/
    public synchronized void deleteThreadGroup(String nodeName, String lugrpName) {
    	adapter_logger.warn("SNAAdapter:deleteThreadGroup> [" + adapterGroupName + "] Start To Delete LU Thread Group, NodeName = " + nodeName + ", LU Group Name = " + lugrpName);
    	if (threadNodeMap == null) {
    		adapter_logger.warn("SNAAdapter:deleteThreadGroup> [" + adapterGroupName + "] Thread Node Map is NULL, so Return");    		
    		return;
    	}

    	HashMap threadGroupMap = (HashMap)threadNodeMap.get(nodeName);
        if (threadGroupMap == null) {
    		adapter_logger.warn("SNAAdapter:deleteThreadGroup> [" + adapterGroupName + "] Thread Group Map is NULL, so Return, NodeName = " + nodeName);
    		return;
        }
	    
        threadGroupMap.remove(lugrpName);

    	adapter_logger.warn("SNAAdapter:deleteThreadGroup> [" + adapterGroupName + "] Success To Delete LU Thread Group, NodeName = " + nodeName + ", LU Group Name = " + lugrpName);    	
    }
    
    
    /** 
     * 1. 기능 :  WCA 프라퍼티 정보 갱신 처리
     * 2. 처리 개요 : 
     * 3. 주의사항
     **/    
    public void updateWcaInfo() throws WCAException {               
    	// 0. 새롭게 LU 그룹 정보 맵을 로딩하고, 기존 어댑터 그룹에 해당하는 LU 그룹 노드 맵을 구함
        HashMap newLuGrpNodeMap = loadAdapterInfo(adapterGroupName, adapterName);
    	HashMap oldLuGrpNodeMap = lugrpNodeMap;
    	
    	if (newLuGrpNodeMap == null) {
    		throw new WCAException("LuGrpNodeMap not found adapterGroupName=" + adapterGroupName + ", adapterName=" + adapterName);
    	}
    	// 1. 기존에는 있었으나, 현재에 삭제된 것을 찾아서 종료/삭제 처리
    	try {
	    	Iterator iterOldNode = oldLuGrpNodeMap.keySet().iterator();
	        while (iterOldNode.hasNext()) {
	        	String 		sOldNodeName = (String)iterOldNode.next();       	
	        	HashMap 	oldLuGrpGroupNap = (HashMap)oldLuGrpNodeMap.get(sOldNodeName);
	        	Iterator 	iterOldGroup = oldLuGrpGroupNap.keySet().iterator();
	        	while (iterOldGroup.hasNext()) {
	        		String sOldLuGroupName = (String)iterOldGroup.next();
	        		SessionGroupDataEx oLuGrpInfo = (SessionGroupDataEx)oldLuGrpGroupNap.get(sOldLuGroupName);
	        		
	        		// 새로운 LU 그룹 맵에서 기존 정보를 찾음
	        		SessionGroupDataEx oDeleteLuGrpInfo = getLuGroupInfo(newLuGrpNodeMap, oLuGrpInfo.getNodeName(), oLuGrpInfo.getGroupName());
	        		
	        		// 새로운 LU 그룹에 없는 경우는 종료하고 삭제해야 할 대상임
	        		if (oDeleteLuGrpInfo == null) {
	        			adapter_logger.warn("SNAAdapter:updateWcaInfo> [" + adapterName + "] Delete LU Group = [" 
	        					+ oLuGrpInfo.getNodeName() + "][" + oLuGrpInfo.getGroupName() + "]");
	        			
	        			// 종료하고, 삭제        			
	        			deleteLuGroupInfoEx(oLuGrpInfo); 
	        		}
	        	}        	
	        }
    	}
    	catch (Exception e) {
    		adapter_logger.warn("SNAAdapter:updateWcaInfo> [" + adapterGroupName + "] Delete LU Group Exception", e);    		         		
    	}
        
    	// 2. 기존과 현재에 모두 있는 경우 업데이트 처리    	
    	try {
	        // 기존에도 있어고 현재에도 있는 것들은 정보가 갱신된 경우 처리
	        Iterator iterNewNode = newLuGrpNodeMap.keySet().iterator();
	        while (iterNewNode.hasNext()) {
	        	String 		sNewNodeName = (String)iterNewNode.next();       	
	        	HashMap 	newLuGrpGroupMap = (HashMap)newLuGrpNodeMap.get(sNewNodeName);
	        	Iterator 	iterNewGroup = newLuGrpGroupMap.keySet().iterator();
	        	while (iterNewGroup.hasNext()) {
	        		String sNewLuGroupName = (String)iterNewGroup.next();
	        		SessionGroupDataEx oNewLuGroupInfo = (SessionGroupDataEx)newLuGrpGroupMap.get(sNewLuGroupName);
	        		
	        		// 기존 LU 그룹 정보에서 갱신된 LU 그룹 정보에 해당하는 것을 찾지 못한 경우, Continue
	        		SessionGroupDataEx oOldLuGroupInfo = getLuGroupInfo(lugrpNodeMap, oNewLuGroupInfo.getNodeName(), oNewLuGroupInfo.getGroupName());
	        		if (oOldLuGroupInfo == null) {
	        			// 삭제 대상은 3단계에서 처리
	        			continue;
	        		}

	        		adapter_logger.warn("SNAAdapter:updateWcaInfo> [" + adapterGroupName + "] Update LU Group = [" 
        					+ oOldLuGroupInfo.getNodeName() + "][" + oOldLuGroupInfo.getGroupName() + "]");
	        		
	        		WCASessionPool wcaLocalSessionPool = null;
	        		WCASessionPool wcaBackupSessionPool = null;

	        		// [바로 갱신이 가능한 것들 처리]
	        		// 로깅 정보 갱신(로그 여부, 로그 레벨, 로그 사이즈 갱신
	        		// 트랜잭션 타임아웃 (tran_timeout) 갱신
	        		// 백업 자동 전환 모드(backup_cvs_mode), 백업 전환 가능 여부(bBackupEnableFlag) 갱신	        		

	        		// [바로 갱신이 불가능한 것들 처리]
	        		// 소프트링크 IP 주소(softLinkSevrName), 소프트링크 포트(port) 갱신
	        		// 백업 노드 이름(backup_node_name), 백업 노트 포트 번호(backup_port) 갱신
	        		// 세션 연결 갯수(sess_cnt) 갱신
	        		
	        		// 로컬 세션 풀 정보 갱신
	        		wcaLocalSessionPool = getLocalSessionPool(oOldLuGroupInfo.getNodeName(), oOldLuGroupInfo.getGroupName());
	        		if (wcaLocalSessionPool != null) {
	        			wcaLocalSessionPool.updateLuGroupInfoEx(oNewLuGroupInfo);
	        		}
	        		
	        		// 백업 세션 풀 정보 갱신
	        		wcaBackupSessionPool = getBackupSessionPool(oOldLuGroupInfo.getNodeName(), oOldLuGroupInfo.getGroupName());
	        		if (wcaBackupSessionPool != null) {
	        			wcaBackupSessionPool.updateLuGroupInfoEx(oNewLuGroupInfo);	        			
	        		}
	        	}        	
	        }
    	}
    	catch (Exception e) {
    		adapter_logger.warn("SNAAdapter:updateWcaInfo> [" + adapterGroupName + "] Update LU Group Exception, Msg = " + e.getMessage(), e);
    	}
        
    	// 3. 기존에는 없었으나, 현재에 추가된 것을 찾아서 추가 처리
    	try {
	    	Iterator iterNewNode = newLuGrpNodeMap.keySet().iterator();
	        while (iterNewNode.hasNext()) {
	        	String 		sNewNodeName = (String)iterNewNode.next();
	        	HashMap 	newLuGrpGroupMap = (HashMap)newLuGrpNodeMap.get(sNewNodeName);
	        	Iterator 	iterMewGroup = newLuGrpGroupMap.keySet().iterator();
	        	while (iterMewGroup.hasNext()) {
	        		String sNewLuGroupName = (String)iterMewGroup.next();
	        		SessionGroupDataEx oNewLuGrpInfo = (SessionGroupDataEx)newLuGrpGroupMap.get(sNewLuGroupName);
	        		
	        		// 기존 LU 그룹 맵에서 해당하는 정보를 찾음
	        		SessionGroupDataEx oOldLuGrpInfo = getLuGroupInfo(oldLuGrpNodeMap, oNewLuGrpInfo.getNodeName(), oNewLuGrpInfo.getGroupName());
	        		
	        		// 기존에 LU 그룹맵에 없는 경우 추가해야 할 대상임
	        		if (oOldLuGrpInfo == null) {
	        			adapter_logger.warn("SNAAdapter:updateWcaInfo> [" + adapterGroupName + "] Insert LU Group = [" 
	        					+ oNewLuGrpInfo.getNodeName() + "][" + oNewLuGrpInfo.getGroupName() + "]");        			
	        			// oNewLuGrpInfo 정보를 이용해서 추가 하고, 시작
	        			insertLuGroupInfoEx(oNewLuGrpInfo);
	        		}
	        	}        	
	        }        
    	}
    	catch (Exception e) {
    		adapter_logger.warn("SNAAdapter:updateWcaInfo> [" + adapterGroupName + "] Insert LU Group Exception, Msg = " + e.getMessage(), e);    		    		
    	}

    	return;
    }
    
    /** 
     * 1. 기능 : 어댑터 정보 반환
     * 2. 처리 개요 : 어댑터 그룹 이름을 반환한다.
     * 3. 주의사항 
     * 
     * @return 어댑터 정보
     **/
    public String toString() {
        return " " + adapterGroupName + " ";
    }
    
    /** 
     * 1. 기능 :  어댑터 그룹에 해당하는 모든 LU 세션 정보 출력
     * 2. 처리 개요 :
     * 3. 주의사항 
     **/
    public void printLuGroupInfo() {        
    	if (lugrpNodeMap == null) {
    		adapter_logger.warn("SNAAdapter:printLuGroupInfo> lugrpNodeMap is null");    		
    		return;
    	}
         
        Iterator iterNode = lugrpNodeMap.keySet().iterator();
        while (iterNode.hasNext()) {
        	String nodeName = (String)iterNode.next();
        	printLuGroupInfo(nodeName);         	
        }
        
        return;
    }
     
     
    /** 
     * 1. 기능 :  특정 노드의 LU 그룹에 해당하는 LU 세션 정보 출력
     * 2. 처리 개요 :
     * 3. 주의사항 
     * 
     * @param	 sNodeName			Softlink Node Name
     **/    
    public void printLuGroupInfo(String nodeName) {        
    	if (lugrpNodeMap == null) {
     		adapter_logger.warn("SNAAdapter:printLuGroupInfo> lugrpNodeMap is null");
         	return;     		
        }
         
        HashMap lugrpGroupMap = (HashMap)lugrpNodeMap.get(nodeName);
        if (lugrpGroupMap == null) {
        	adapter_logger.warn("SNAAdapter:printLuGroupInfo> lugrpGroupMap is null, NodeName = " + nodeName);
        	return;        	
        }
             	
     	Iterator iterGroup = lugrpGroupMap.keySet().iterator();
     	while (iterGroup.hasNext()) {       	
     		String lugrpName = (String)iterGroup.next();
     		printLuGroupInfo(nodeName, lugrpName);
     	}
    }    
     
    /** 
     * 1. 기능 :  어댑터 그룹, LU 그룹에 해당하는 LU 세션 정보 출력
     * 2. 처리 개요 :
     * 3. 주의사항 
     * 
     * @param	sNodeName			Softlink Node Name
     * @param   sGroupCode          LU 그룹 이름
     **/
    public void printLuGroupInfo(String nodeName, String lugrpName) {
    	if (lugrpNodeMap == null) {
    		adapter_logger.warn("SNAAdapter:printLuGroupInfo> lugrpNodeMap is null");
    		return;     		
    	}
          
    	HashMap lugrpGroupMap = (HashMap)lugrpNodeMap.get(nodeName);
        if (lugrpGroupMap == null) {
        	adapter_logger.warn("SNAAdapter:printLuGroupInfo> lugrpGroupMap is null, NodeName = " + nodeName);        	
        	return;       
        }
          
        SessionGroupDataEx lugrpInfo  = (SessionGroupDataEx)lugrpGroupMap.get(lugrpName);
        if (lugrpInfo == null) {
        	adapter_logger.warn("SNAAdapter:printLuGroupInfo> lugrpGroupMap is null, NodeName = " + nodeName + ", LU Group Name = " + lugrpName);        	
        	return;     
        }
          
        if (adapter_logger.isDebug()) {  
        	adapter_logger.debug(lugrpInfo.toString());
        }
    }
    
    /** 
     * 1. 기능 :  SNA 어댑터에 속한 모든(로컬/백업) WCA Session Pool에 대해서 INBOUND 핸들러 등록
     * 2. 처리 개요 : 
     * 3. 주의사항
     * 
     * @param objHandler INBOUND 핸들러 instance
     **/
    public void registerEventHandler(Object objHandler) {
        int					i = 0;
        WCASessionPool		wcaSessionPool;
        
        adapter_logger.warn("SNAAdapter:registerEventHandler> [" + adapterGroupName + "] Start to Register Event Handler");
        
        adapter_logger.warn("SNAAdapter:registerEventHandler> [" + adapterGroupName + "] Start to Register Event Handler For Local Session Pool List, Size = " + listWCALocalSessonPool.size());        
        for (i = 0; i < listWCALocalSessonPool.size(); i++) {
            wcaSessionPool = (WCASessionPool)listWCALocalSessonPool.get(i);
        	wcaSessionPool.registerEventHandler(objHandler);
    	}
    
        adapter_logger.warn("SNAAdapter:registerEventHandler> [" + adapterGroupName + "] Start to Register Event Handler For Backup Session Pool List, Size = " + listWCABackupSessonPool.size());        
	    for (i = 0; i < listWCABackupSessonPool.size(); i++) {
	        wcaSessionPool = (WCASessionPool)listWCABackupSessonPool.get(i);
	    	wcaSessionPool.registerEventHandler(objHandler);
		}
    
	    adapter_logger.warn("SNAAdapter:registerEventHandler> [" + adapterGroupName + "] Success to Register Event Handler");
	    
	    return;
    }    
}



