package com.eactive.eai.adapter.socket.config;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.Timer;

import com.eactive.eai.adapter.AdapterGroupVO;
import com.eactive.eai.adapter.AdapterManager;
import com.eactive.eai.adapter.AdapterVO;
import com.eactive.eai.adapter.socket.common.CommonLib;
import com.eactive.eai.adapter.socket.service.AdminTable;
import com.eactive.eai.adapter.socket.service.ConnectionManager;
import com.eactive.eai.adapter.socket.service.InboundClient;
import com.eactive.eai.adapter.socket.service.OutboundChannelManager;
import com.eactive.eai.adapter.socket.service.RecoveryManager;
import com.eactive.eai.adapter.socket.service.SessionChannel;
import com.eactive.eai.adapter.socket.service.SessionManager;
import com.eactive.eai.adapter.socket.service.SessionService;
import com.eactive.eai.adapter.socket.service.SocketLogManager;
import com.eactive.eai.adapter.socket.service.SocketServer;
import com.eactive.eai.adapter.socket.service.SocketService;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.util.Logger;

public class SocketAdapterManager {

    public static long  CONNECTION_CHECK_CYCLE      = 60 * 1000L;       // every 60 sec.
    public static long  RECOVERY_CHECK_CYCLE        = 15 * 1000L;       // every 15 sec.
    public static long  UNUSED_CONNECTION_TIMEOUT   = 0L;               // 10 * 60 * 1000L;
                                                                        // 10분간 미상용시 Connection Close;
                                                                        // 0으로 설정시 강제적으로 미사용 소켓을 CLOSE하지 않음....
    private static SocketAdapterManager instance    = new SocketAdapterManager();
    private Logger                      logger      = SocketLogManager.getInstance().getLogger();

    private HashMap<String, LinkedList<SocketService>> stateTable;	 // Connection 정보를 AdapterGroup별 I/O BOUND별 Socket reference를 같는다.
    private LinkedList<SocketService>                  errorTable;  // Client Socket의 Connection 에러에 대한 내용을 관리한다.
    private HashMap<String, LinkedList<SessionService>>sessionTable;// Inbound Session관리를 위한 Table

    private ConnectionManager           connManager;
    private RecoveryManager             recoverManager;
    private	SessionManager              sessionManager;

    private Timer                       connTimer;
    private Timer                       recoverTimer;

    private boolean                     started;
    // TO Support STOP & START by Adapter
    // KEY = AdapterName, Value=AdminTable for stop/start
    private HashMap<String, AdminTable> adminTableMap;
    // KEY = RemoteIP:Port, Value=SocketService Object;
    private HashMap<String, SocketService>controlMap;

    private SocketAdapterManager() {
        stateTable      = new HashMap<String, LinkedList<SocketService>>();
        errorTable      = new LinkedList<SocketService>();
        sessionTable    = new HashMap<String, LinkedList<SessionService>>();
        adminTableMap   = new HashMap<String, AdminTable>();
        controlMap      = new HashMap<String, SocketService>();

        connManager     = null;
        recoverManager  = null;
        sessionManager  = SessionManager.getInstance();
        started         = false;
    }

    public static SocketAdapterManager getInstance() {
        return instance;
    }

    public void start() {
        started = true;
    }

	public HashMap<String, LinkedList<SocketService>> getAllSessions() {
		return this.stateTable;
	}

    private void startManagers() {
        if(logger.isInfoEnabled()){
            this.logger.info( CommonLib.getMessage("BICEAIMSA001") );
        }

        // Connection Manager starting...
        stateTable.clear();
        if(logger.isInfoEnabled()){
            this.logger.info( CommonLib.getMessage("BICEAIMSA002") );
        }
        this.connManager	= new ConnectionManager( stateTable );
        this.connTimer    = new Timer();
        this.connTimer.schedule( this.connManager, CONNECTION_CHECK_CYCLE, CONNECTION_CHECK_CYCLE  );
        if(logger.isInfoEnabled()){
            this.logger.info( CommonLib.getMessage("BICEAIMSA003") );
        }

        // Recovery Manager starting...
        errorTable.clear();
        if(logger.isInfoEnabled()){
            this.logger.info( CommonLib.getMessage("BICEAIMSA004") );
        }
        this.recoverManager = new RecoveryManager( errorTable );
        this.recoverTimer   = new Timer();
        this.recoverTimer.schedule( this.recoverManager, RECOVERY_CHECK_CYCLE, RECOVERY_CHECK_CYCLE  );
        if(logger.isInfoEnabled()){
            this.logger.info( CommonLib.getMessage("BICEAIMSA005") );
        }

        // Session Manager starting...
        this.sessionManager.removeAll();
        this.sessionTable.clear();
        if(logger.isInfoEnabled()){
            this.logger.info( CommonLib.getMessage("BICEAIMSA006") );
        }
        // OUTBOUND Connection POOL CLEAR
        OutboundChannelManager.getInstance().removeAll();
        if(logger.isInfoEnabled()){
            this.logger.info( CommonLib.getMessage("BICEAIMSA007") );
        }

        started = true;

        if(logger.isInfoEnabled()){
            this.logger.info( CommonLib.getMessage("BICEAIMSA008") );
        }
		//this.logger.info("Socket adapter manager started...");
    }

    // return SocketService Object list....
    @SuppressWarnings({ "rawtypes", "unchecked" })
	private Object[] getAllAdminList() {
        // AdminTable Object list
        Object[] adminTables    = adminTableMap.values().toArray();
        LinkedList serviceList  = new LinkedList();

        for ( int i = 0; i < adminTables.length; i++ ) {
            AdminTable adminTable =  (AdminTable) adminTables[i];
            Object[] services = adminTable.getConnections();
            for ( int j=0; j < services.length; j++ ) {
                serviceList.add( services[j] );
            }
        }

        return serviceList.toArray();
    }

    private Object[] getAdminList( String adapterName ) {
        AdminTable adminTable = (AdminTable) adminTableMap.get( adapterName );
        if ( adminTable == null ) return null;
        return adminTable.getConnections();
    }

    // DEUBG LEVEL 조정 또는 MAXCONNECTION MAXSESSION 수 조정용...
    public ConfigurationContext getContext( String adapterName ) {
        AdminTable adminTable = (AdminTable) adminTableMap.get( adapterName );
        if ( adminTable == null ) return null;
        return adminTable.getContext();
    }

    public void setContext( ConfigurationContext context ) {
        AdminTable adminTable = (AdminTable) adminTableMap.get( context.getAdapterName() );
        if ( adminTable == null ) return;
        adminTable.setContext( context );
    }

    // Group별 Session수 조정을 위한 인터페이스
    public String  adjustSession( String adapterName, int newSessions ) throws Exception {
        StringBuffer    strBuff = new StringBuffer();
        AdminTable adminTable = (AdminTable) adminTableMap.get( adapterName );

        if ( adminTable == null ) {
            strBuff.append( CommonLib.getMessage("BECEAIMSA001", new String[] { adapterName } ));
            return strBuff.toString();
        }

        String adapterGroupName = adminTable.getAdapterGroupName();
        ConfigurationContext ctx = adminTable.getContext();
        if ( ctx.getBoundUsage().equals( ConfigurationContext.OUTBOUND_SOCKET ) ) {
            strBuff.append( CommonLib.getMessage("BECEAIMSA002", new String[] { adapterName } ));
            return strBuff.toString();
        }

        SessionChannel sChannel = SessionManager.getInstance().getChannel( adapterGroupName );

        if ( sChannel == null ) {
            strBuff.append( CommonLib.getMessage("BECEAIMSA019", new String[] { adapterName } ));
            return strBuff.toString();
        }
        
        int oldSessions = sChannel.getMaxNumOfSessions();

        // Sessions수는 Max값에 대해 큰값만 지원한다.
        if ( oldSessions > newSessions ) {
            strBuff.append( CommonLib.getMessage("BWCEAIMSA001", new String[] { adapterName, Integer.toString(oldSessions) } ));
            return strBuff.toString();
        } else if ( oldSessions == newSessions ) {
            strBuff.append( CommonLib.getMessage("BICEAIMSA009", new String[] { adapterName, Integer.toString(oldSessions) } ));
            return strBuff.toString();
        }
        //
        sChannel.setMaxNumOfSessions( newSessions );
        for ( int i = oldSessions+1; i <= newSessions; i++ ) {
//            SessionService session	= new SessionService( adapterGroupName, true, i, adminTable.getContext().getTraceLevel() );
            SessionService session	= new SessionService( adapterGroupName, true, i );
            session.start();
        }
        strBuff.append( CommonLib.getMessage("BICEAIMSA010", new String[] { adapterName, Integer.toString(oldSessions), Integer.toString(newSessions) } ));
        return strBuff.toString();
    }

    // Connection 수 조정을 위한 작업
    public String adjustConnection( String adapterName, int newConnections ) {
        StringBuffer strBuff = new StringBuffer();

        AdminTable adminTable = (AdminTable) adminTableMap.get( adapterName );
        if ( adminTable == null ) {
            strBuff.append( CommonLib.getMessage("BECEAIMSA003", new String[] { adapterName } ));
            return strBuff.toString();
        }
        //String key  = adminTable.getAdapterGroupName();
        // Connection수는 Max값에 대해 큰값만 지원한다.
        ConfigurationContext context = adminTable.getContext();
        int oldConnections = context.getMaxConnection();

        if ( oldConnections > newConnections ) {
            strBuff.append( CommonLib.getMessage("BWCEAIMSA002", new String[] { adapterName, Integer.toString(oldConnections) } ));
            return strBuff.toString();
        }

        context.setMaxConnection( newConnections );

        if ( context.getSocketType().equals( ConfigurationContext.SERVER_SOCKET ) ) {
            strBuff.append( CommonLib.getMessage("BICEAIMSA011", new String[] { adapterName, Integer.toString(oldConnections), Integer.toString(newConnections) } ));
            return strBuff.toString();
        }

        // Client Socket Type에 대해서 Connection 수를 증가 시킨다....
        //  INBOUND & OUTBOUND

        if ( context.getBoundUsage().equals( ConfigurationContext.INBOUND_SOCKET ) ) {
            // CLIENT TYPE의 Socket에 대해 MAX CONNECTION 수 만큼 미리 CONNECTION을 맺는다.
            for ( int j=oldConnections; j < newConnections; j++ ) {
                InboundClient aService = new InboundClient( context );
                adminTable.addConnection( aService );
                aService.start();
            }
        // OUTBOUND SOCKET CONFIGURATION
//        } else if ( context.getBoundUsage().equals( ConfigurationContext.OUTBOUND_SOCKET ) ) {
//            // CLIENT TYPE의 Socket에 대해 MAX CONNECTION 수 만큼 미리 CONNECTION을 맺는다.
//            for ( int j=oldConnections; j < newConnections; j++ ) {
//                OutboundClient aService = new OutboundClient( context );
//                adminTable.addConnection( aService );
//                aService.start();
//            }
        } // End of Connection 증가...

        strBuff.append( CommonLib.getMessage("BICEAIMSA011", new String[] { adapterName, Integer.toString(oldConnections), Integer.toString(newConnections) } ));
        return strBuff.toString();
    }

    public long getTimeout( String adapterGroupName ) {
        long    timeout = 3000L;        // default Timeout Time

        try {
        	LinkedList<SocketService> list = stateTable.get( adapterGroupName );
            if ( list == null ) {
                return timeout;
            }

            if ( list.size() == 0 ) return timeout;

            SocketService cInfo = list.getFirst();

            timeout = cInfo.getContext().getSessionTimeout() * 1000L;
        } catch( Exception e ) {}

        return  timeout;
    }

    /**
     * Adapter Name별 Socket Adapter starting...
     *
     */
    public String start( String adapterGroupName, String adapterName ) {
        StringBuffer strBuff = new StringBuffer();
        AdapterManager  aMgr    = AdapterManager.getInstance();
        AdapterVO       config  = aMgr.getAdapterVO(adapterGroupName, adapterName);

        if ( config == null ) {
            strBuff.append( CommonLib.getMessage("BECEAIMSA004", new String[] { adapterGroupName, adapterName } ));
            return strBuff.toString();
        }

        if ( config.isStarted() ) {
            strBuff.append( CommonLib.getMessage("BWCEAIMSA003", new String[] { adapterGroupName, adapterName } ));
            return strBuff.toString();
        }

        try {
            if(logger.isInfoEnabled()){
                this.logger.info( CommonLib.getMessage("BICEAIMSA012", new String[] { adapterGroupName, adapterName } ));
            }

            config.start();

            strBuff.append( CommonLib.getMessage("BICEAIMSA013", new String[] { adapterGroupName, adapterName } ));
            if(logger.isInfoEnabled()){
                this.logger.info( strBuff.toString() );
            }

        } catch( Exception e ) {
            strBuff.append( CommonLib.getMessage("BECEAIMSA020", new String[] { adapterGroupName, adapterName, e.getMessage() } ));

            logger.error( strBuff.toString(), e );
        }

        return strBuff.toString();
    }

    public String stop( String adapterGroupName, String adapterName ) {
        StringBuffer strBuff = new StringBuffer();
        AdapterManager  aMgr    = AdapterManager.getInstance();
        AdapterVO       config  = aMgr.getAdapterVO(adapterGroupName, adapterName);

        if ( config == null ) {
            strBuff.append( CommonLib.getMessage("BECEAIMSA005", new String[] { adapterGroupName, adapterName } ));
            return strBuff.toString();
        }
         if ( !config.isStarted() ) {
            strBuff.append( CommonLib.getMessage("BWCEAIMSA004", new String[] { adapterGroupName, adapterName } ));
            return strBuff.toString();
        }

        try {
            if(logger.isInfoEnabled()){
                this.logger.info( CommonLib.getMessage("BICEAIMSA014", new String[] { adapterGroupName, adapterName } ));
            }
            config.stop();
            strBuff.append( CommonLib.getMessage("BICEAIMSA015", new String[] { adapterGroupName, adapterName } ));
            if(logger.isInfoEnabled()){
                this.logger.info( strBuff.toString() );
            }

        } catch( Exception e ) {
            strBuff.append( CommonLib.getMessage("BECEAIMSA021", new String[] { adapterGroupName, adapterName, e.getMessage() } ));

            logger.error( strBuff.toString(), e );
        }

        return strBuff.toString();
    }

    // START A SOCKET ADAPTER CONFIGURATION
    public void start(AdapterVO config) throws Exception {

        ConfigurationContext context =  null;

        if ( !started ) {
            startManagers();
        }

        try {
            // Configuration Context 생성
            logger.info("createContext start : GROUP[" + config.getAdapterGroupName() + "] -> ADAPTER[" + config.getName() + "]");
            context = createContext(config);
            logger.info("createContext end ===============================================================");
            config.setContext(context);

            // adminTable CONFIGURATION
            String	key           = context.getAdapterGroupName();
            String  adapterName   = context.getAdapterName();
            AdminTable adminTable = adminTableMap.get( adapterName );

            //socket START 설정
            context.setStarted( true );

            if ( adminTable == null ) {
                adminTable = new AdminTable( key, adapterName, context );
                adminTableMap.put( adapterName, adminTable );
            } else {
                adminTable.setContext( context );
                adminTable.initialize();
            }

			Object o = stateTable.get( key );
			if ( o == null ) {
				stateTable.put( key, new LinkedList<SocketService>() );
				if ( !context.getSocketType().equals( ConfigurationContext.ADMIN_SOCKET ) ) {
					sessionTable.put( key, new LinkedList<SessionService>() );
				}
			}

            // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  //
            // INBOUND 또는 IOBOUND SOCKET 인 경우 기동처리
            // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  //
            if (   context.getBoundUsage().equals( ConfigurationContext.INBOUND_SOCKET )
                || context.getBoundUsage().equals( ConfigurationContext.IOBOUND_SOCKET ) ) {

                // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //
                // SERVER TYPE의 Socket인 경우
                // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //
                if ( context.getSocketType().equals( ConfigurationContext.SERVER_SOCKET ) ) {
                    SocketServer aService = new SocketServer( context );

                    // SessionChannel : 세션 정보 관리
                    if ( !this.sessionManager.hasChannel( key ) ) {
                        // Create a new SessionChannel : 세션의 기본 세션수, 최대세션수, 세션 타임아웃
                        SessionChannel channel = new SessionChannel( context.getDefaultSession(), context.getMaxSession(), context.getSessionTimeout() );
                        // SessionManager에 해당 AdapterGroup의 Session정보 등록
                        sessionManager.start( key, channel );

                        // Session Starting.....
                        for ( int k=0; k < context.getMaxSession(); k++ ) {
//                            SessionService session	= new SessionService( context.getAdapterGroupName(), true, (k+1), context.getTraceLevel() );
                            SessionService session	= new SessionService( context.getAdapterGroupName(), true, (k+1));
                            // 쓰레드가 시작되면, SocketAdapterManager를 통해 SessionManager의 pool 에 add 한다.
                            session.start();
                        }
                    }

                    // adminTable의 connections 벡터에 서버소켓 스레드 인스탄스 등록..
                    adminTable.addConnection( aService );

					aService.start();

                // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //
                // CLIENT TYPE의 Socket인 경우
                // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //
                } else if( context.getSocketType().equals( ConfigurationContext.CLIENT_SOCKET ) ) {
                    // CLIENT TYPE의 Socket에 대해 MAX CONNECTION 수 만큼 미리 CONNECTION을 맺는다.
                    for ( int j=0; j < context.getMaxConnection(); j++ ) {
                        // 해당 그룹의 SessionChannel을 가지고 있는가 ?
                        if ( !this.sessionManager.hasChannel( key ) ) {
                            // Create a new SessionChannel
                            SessionChannel channel = new SessionChannel( context.getDefaultSession(), context.getMaxSession(), context.getSessionTimeout() );
                            // SessionManager에 해당 AdapterGroup의 Session정보 등록
                            sessionManager.start( key, channel );
                            // Session Starting.....
                            for ( int k=0; k < context.getMaxSession(); k++ ) {
//                                SessionService session	= new SessionService( context.getAdapterGroupName(), true, (k+1), context.getTraceLevel() );
                                SessionService session	= new SessionService( context.getAdapterGroupName(), true, (k+1));
                                session.start();
                            }
                        }
                        if ( context.getBoundUsage().equals( ConfigurationContext.INBOUND_SOCKET ) ) {
                            InboundClient aService = null;
                            aService = new InboundClient( context );
                            adminTable.addConnection( aService );
                            aService.start();
//                        } else {
//                            if ( !OutboundChannelManager.getInstance().hasChannel( key )) {
//                                OutboundChannel channel = new OutboundChannel(context.getSessionTimeout()*1000);
//                                OutboundChannelManager.getInstance().addConnectionGroup( key, channel );
//                            }
//                            ReaderClient aService = new ReaderClient( context );
//                            adminTable.addConnection( aService );
//                            aService.start();
                        }
                    } // end of for
                } else {
                    // AdminServer로 변경필요...
                    SocketServer aService = new SocketServer( context );
                    adminTable.addConnection( aService );
                    aService.start();
                }

			// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  //
			// OUTBOUND 인 경우 기동처리
			// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  //
			} else if ( context.getBoundUsage().equals( ConfigurationContext.OUTBOUND_SOCKET ) ) {

				// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //
				// SERVER TYPE의 Socket인 경우
				// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //
				if ( context.getSocketType().equals( ConfigurationContext.SERVER_SOCKET ) ) {
					SocketServer aService = new SocketServer( context );
					adminTable.addConnection( aService );
					aService.start();

				// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //
                // CLIENT TYPE의 Socket인 경우
                // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //
				} else {
//					// CLIENT TYPE의 Socket에 대해 MAX CONNECTION 수 만큼 미리 CONNECTION을 맺는다.
//                    for ( int j=0; j < context.getMaxConnection(); j++ ) {
//                        OutboundClient aService = null;
//                        aService = new OutboundClient( context );
//                        if ( !OutboundChannelManager.getInstance().hasChannel( key )) {
//                            OutboundChannel channel = new OutboundChannel(context.getSessionTimeout()*1000);
//                            OutboundChannelManager.getInstance().addConnectionGroup( key, channel );
//                        }
//                        adminTable.addConnection( aService );
//                        aService.start();
//                    }
                }
            }

        } catch ( Exception e ) {
            String errMsg = CommonLib.getMessage("BECEAIMSA020", new String[]{ config.getAdapterGroupName(), config.getName(), e.getMessage()});
            logger.error( errMsg, e );
            throw new Exception( errMsg );
        }
    }

    public  void changeAdapterStatus(SocketService service, boolean on) {
        AdapterManager  aMgr    = AdapterManager.getInstance();
        AdapterVO       config  = aMgr.getAdapterVO(service.getContext().getAdapterGroupName(), service.getContext().getAdapterName());
        if ( config == null ) return;

        config.setStatus( on );
    }

    private ConfigurationContext createContext( AdapterVO  config ) throws Exception {
        return createContext( config, false );
    }

    private ConfigurationContext createContext( AdapterVO  config, boolean flag ) throws Exception {

        ConfigurationContext context = new ConfigurationContext();
		context.setAdapterVO(config);

        context.setAdapterGroupName( config.getAdapterGroupName() );
        context.setAdapterName( config.getName() );

        PropManager manager = PropManager.getInstance();
        Properties  prop    = manager.getProperties( config.getPropGroupName() );
        String      errMsg  = null;

        if ( prop.getProperty(Keys.ADAPTER_GROUP_NAME) == null ) {
            errMsg = CommonLib.getMessage("BECEAIMSA016", new String[]{"Socket Adapter그룹명", "adapter.group.name"} );

            logger.error( errMsg );

            throw new Exception( errMsg );
        }

        context.setBoundUsage( prop.getProperty(Keys.BOUND_USAGE, context.getBoundUsage()) );
        if ( "INBOUND,OUTBOUND,IOBOUND".indexOf(context.getBoundUsage()) < 0 ) {
            errMsg = CommonLib.getMessage("BECEAIMSA017", new String[]{Keys.BOUND_USAGE, context.getBoundUsage(), "[ INBOUND | OUTBOUND | IOBOUND ]" } );

            logger.error( errMsg );

            throw new Exception( errMsg );
        }

        context.setSocketType( prop.getProperty(Keys.SOCKET_TYPE, context.getSocketType()) );
        if ( "SERVER, CLIENT, ADMIN".indexOf( context.getSocketType() ) < 0 ) {
            errMsg = CommonLib.getMessage("BECEAIMSA017", new String[]{Keys.SOCKET_TYPE, context.getSocketType(), "[ SERVER | CLIENT | ADMIN ]" } );

            logger.error( errMsg );

            throw new Exception( errMsg );
        }


        //====================================================================================================================
        //서버 IP 설정
        //TSEAIBJ05 테이블의 'SysAnsrLnkgIP' 필드값 사용시 (TSEAIBJ05에 정보가 등록 안된경우 Property 정보 사용)
        if (config.getBatchServerIP() != null) {
            context.setHostName( config.getBatchServerIP().trim() );

        } else {
            context.setHostName( prop.getProperty(Keys.HOST_NAME, context.getHostName()) );
        }

        //서버 PortNo 설정
        //TSEAIBJ05 테이블의 'LnkgPortInfoName' 필드값 사용시 (TSEAIBJ05에 정보가 등록 안된경우 Property 정보 사용)
        if (config.getBatchServerPortNo() != null) {
            context.setPortNumber( Integer.parseInt( config.getBatchServerPortNo().trim() ) );

        } else {
            context.setPortNumber( Integer.parseInt( prop.getProperty(Keys.PORT_NUMBER, Integer.toString(context.getPortNumber()))) );
        }
        //====================================================================================================================


        context.setLocalHostName( prop.getProperty(Keys.LOCAL_HOST_NAME, context.getLocalHostName()) );
        context.setLocalPortNumber( Integer.parseInt( prop.getProperty(Keys.LOCAL_PORT_NUMBER, Integer.toString(context.getLocalPortNumber() )) ));

        try {
            if ( context.getSocketType().equals(  ConfigurationContext.CLIENT_SOCKET ) ) {
                context.setMaxConnection( Integer.parseInt( prop.getProperty(Keys.MAX_CONNECTION, Integer.toString( context.getMaxConnection()))));
                if ( context.getMaxConnection() <= 0 ) {
                    errMsg = CommonLib.getMessage("BECEAIMSA017", new String[]{Keys.MAX_CONNECTION, Integer.toString( context.getMaxConnection() ), "[ CLIENT TYPE의 Sokcet인 경우 0 보다 크다 ]" } );

                    logger.error( errMsg );

                    throw new Exception( errMsg );
                }

            } else {
                context.setMaxConnection( Integer.parseInt( prop.getProperty(Keys.MAX_CONNECTION, "0")) );
                if ( context.getMaxConnection() < 0 ) {
                    errMsg = CommonLib.getMessage("BECEAIMSA017", new String[]{Keys.MAX_CONNECTION, Integer.toString( context.getMaxConnection() ), "[ SERVER TYPE의 Socket인 경우 0 보다 크거나 같다 ]" } );

                    logger.error( errMsg );

                    throw new Exception( errMsg );
                }
            }
        } catch( NumberFormatException e ) {
            errMsg = CommonLib.getMessage("BECEAIMSA017", new String[]{Keys.MAX_CONNECTION, prop.getProperty(Keys.MAX_CONNECTION), "[ 0 보다 크거나 같다 ]" } );

            logger.error( errMsg );

            throw new Exception( errMsg );
        }

        try {
            context.setConnLimitPerIp( Integer.parseInt( prop.getProperty(Keys.CONN_LIMIT_PER_IP, Integer.toString( context.getConnLimitPerIp()))) );
        } catch( NumberFormatException e ) {
            errMsg = CommonLib.getMessage("BECEAIMSA017", new String[]{Keys.CONN_LIMIT_PER_IP, prop.getProperty(Keys.DEFAULT_SESSION), "[ 0 보다 크거나 같다 ]" } );

            logger.error( errMsg );

            throw new Exception( errMsg );
        }

        try {
            context.setDefaultSession( Integer.parseInt( prop.getProperty(Keys.DEFAULT_SESSION, Integer.toString( context.getDefaultSession()))));
            if ( context.getDefaultSession() <= 0 && "INBOUND,IOBOUND".indexOf( context.getBoundUsage() ) >= 0 ) {
                errMsg = CommonLib.getMessage("BECEAIMSA017", new String[]{Keys.DEFAULT_SESSION, Integer.toString( context.getDefaultSession() ), "[ INBOUND/IOBOUND의 경우 0 보다 크다 ]" } );

                logger.error( errMsg );

                throw new Exception( errMsg );
            }
        } catch( NumberFormatException e ) {
            errMsg = CommonLib.getMessage("BECEAIMSA017", new String[]{Keys.DEFAULT_SESSION, prop.getProperty(Keys.DEFAULT_SESSION), "[ 0 보다 크다 ]" } );
            logger.error( errMsg );
            throw new Exception( errMsg );
        }

        try {
            context.setMaxSession( Integer.parseInt( prop.getProperty(Keys.MAX_SESSION, Integer.toString( context.getMaxSession()))));
            if ( context.getMaxSession() <= 0 && "INBOUND,IOBOUND".indexOf( context.getBoundUsage() ) >= 0 ) {
                errMsg = CommonLib.getMessage("BECEAIMSA017", new String[]{Keys.MAX_SESSION, Integer.toString( context.getMaxSession() ), "[ INBOUND/IOBOUND의 경우 0 보다 크다 ]" } );
                logger.error( errMsg );
                throw new Exception( errMsg );
            }
        } catch( NumberFormatException e ) {
            errMsg = CommonLib.getMessage("BECEAIMSA017", new String[]{Keys.MAX_SESSION, prop.getProperty(Keys.MAX_SESSION), "[ 0 보다 크다 ]" } );
            logger.error( errMsg );
            throw new Exception( errMsg );
        }

        try {
            context.setTimeout( Integer.parseInt( prop.getProperty(Keys.TIMEOUT, Integer.toString( context.getTimeout()))));
            if ( context.getTimeout() < 0 ) {
                errMsg = CommonLib.getMessage("BECEAIMSA017", new String[]{Keys.TIMEOUT, Integer.toString( context.getTimeout() ), "[ 0 보다 크거나 같다 ]" } );
                logger.error( errMsg );
                throw new Exception( errMsg );
            }
        } catch( NumberFormatException e ) {
            errMsg = CommonLib.getMessage("BECEAIMSA017", new String[]{Keys.TIMEOUT, prop.getProperty(Keys.TIMEOUT), "[ 0 보다 크거나 같다 ]" } );
            logger.error( errMsg );
            throw new Exception( errMsg );
        }

        try {
            context.setSessionTimeout( Integer.parseInt( prop.getProperty(Keys.SESSION_TIMEOUT, Integer.toString( context.getSessionTimeout()))));
            //if ( context.getSessionTimeout() < 1 ) {
            //    errMsg = CommonLib.getMessage("BECEAIMSA017", new String[]{Keys.SESSION_TIMEOUT, Integer.toString( context.getSessionTimeout() ), "[ 0 보다 크다 ]" } );
            //    logger.error( errMsg );
            //    throw new Exception( errMsg );
            //}
        } catch( NumberFormatException e ) {
            errMsg = CommonLib.getMessage("BECEAIMSA017", new String[]{Keys.TIMEOUT, prop.getProperty(Keys.SESSION_TIMEOUT), "[ 0 보다 크다 ]" } );
            logger.error( errMsg );
            throw new Exception( errMsg );
        }

        context.setResponseType( prop.getProperty(Keys.RESPONSE_TYPE, context.getResponseType()));
        if ( "SYNC,ASYN".indexOf(context.getResponseType()) < 0 ) {
            errMsg = CommonLib.getMessage("BECEAIMSA017", new String[]{Keys.RESPONSE_TYPE, context.getResponseType(), "[ SYNC | ASYN ]" } );
            logger.error( errMsg );
            throw new Exception( errMsg );
        }
        /*
        if ( context.getBoundUsage().equals( ConfigurationContext.IOBOUND_SOCKET )
             && context.getResponseType().equals( ConfigurationContext.SYNC_MODE ) ) {

            errMsg = CommonLib.getMessage("BECEAIMSA018", new String[]{ context.getResponseType() } );
            logger.error( errMsg );
            throw new Exception( errMsg );
        }
        */

        context.setSocketReuse( prop.getProperty(Keys.SOCKET_REUSE, context.getSocketReuse()));
        if ( "Y,N".indexOf(context.getSocketReuse()) < 0 ) {
            errMsg = CommonLib.getMessage("BECEAIMSA017", new String[]{Keys.SOCKET_REUSE, context.getSocketReuse(), "[ Y | N ]" } );
            logger.error( errMsg );
            throw new Exception( errMsg );
        }

        try {
            context.setLlFieldIndex( Integer.parseInt( prop.getProperty(Keys.LL_FIELD_INDEX, Integer.toString(context.getLlFieldIndex()))));
        } catch( NumberFormatException e ) {
            errMsg = CommonLib.getMessage("BECEAIMSA017", new String[]{Keys.LL_FIELD_INDEX, prop.getProperty(Keys.LL_FIELD_INDEX), "[ 0 | 2 | 4 | 5 | 6 | 7 | 8 ]" } );
            logger.error( errMsg );
            throw new Exception( errMsg );
        }

        try {
            context.setLlFieldLength( Integer.parseInt( prop.getProperty(Keys.LL_FIELD_LENGTH, Integer.toString(context.getLlFieldLength()))));
            //if ( "0,2,4,5,6,7,8".indexOf( Integer.toString( context.getLlFieldLength() ) ) < 0 ) {
            //    errMsg = CommonLib.getMessage("BECEAIMSA017", new String[]{Keys.LL_FIELD_LENGTH, Integer.toString( context.getLlFieldLength() ), "[ 0 | 2 | 4 | 5 | 6 | 7 | 8 ]" } );
            //    logger.error( errMsg );
            //    throw new Exception( errMsg );
            //}
        } catch( NumberFormatException e ) {
            errMsg = CommonLib.getMessage("BECEAIMSA017", new String[]{Keys.LL_FIELD_LENGTH, prop.getProperty(Keys.LL_FIELD_LENGTH), "[ 0 | 2 | 4 | 5 | 6 | 7 | 8 ]" } );
            logger.error( errMsg );
            throw new Exception( errMsg );
        }

        context.setAckProtocol( prop.getProperty(Keys.ACK_PROTOCOL, context.getAckProtocol()));
        if ( "Y,N".indexOf(context.getAckProtocol()) < 0 ) {
            errMsg = CommonLib.getMessage("BECEAIMSA017", new String[]{Keys.ACK_PROTOCOL, context.getAckProtocol(), "[ Y | N ]" } );
            logger.error( errMsg );
            throw new Exception( errMsg );
        }

        context.setBwkClsCd( prop.getProperty(Keys.WORK_CLASS_CODE, context.getBwkClsCd()));
        String strResponseOnly = prop.getProperty(Keys.RESPONSE_ONLY);
        if ( strResponseOnly != null && "y,Y,yes,Yes,YES".indexOf(strResponseOnly) > -1 ){
            context.setResponseOnly(true);
        }

        String strUsePoll = prop.getProperty(Keys.USE_POLLING);
        if ( strUsePoll != null && "y,Y,yes,Yes,YES".indexOf(strUsePoll) > -1 ){
            context.setUsePollMsg(true);
        }
        
        

        try {
            context.setTraceLevel( Integer.parseInt( prop.getProperty(Keys.TRACE_LEVEL, Integer.toString(context.getTraceLevel()))));
            if ( "0,1,2".indexOf( Integer.toString( context.getTraceLevel() ) ) < 0 ) {
                errMsg = CommonLib.getMessage("BECEAIMSA017", new String[]{Keys.TRACE_LEVEL, Integer.toString( context.getTraceLevel() ), "[ 0 | 1 | 2 ]" } );
                logger.error( errMsg );
                throw new Exception( errMsg );
            }
        } catch( NumberFormatException e ) {
            errMsg = CommonLib.getMessage("BECEAIMSA017", new String[]{Keys.TRACE_LEVEL, prop.getProperty(Keys.TRACE_LEVEL), "[ 0 | 1 | 2 ]" } );
            logger.error( errMsg );
            throw new Exception( errMsg );
        }

        context.setDescription( prop.getProperty(Keys.DESCRIPTION, context.getDescription()));

        if ( flag ) {
            if(logger.isInfoEnabled()){
                logger.info( CommonLib.getMessage("BICEAIMSA038", new String[]{ context.toString() }));
            }
        }

        return context;
    }

    public String getConnectionInfo(String adapterGroupName ) {
        StringBuffer strBuff = new StringBuffer();
        if ( adapterGroupName == null || adapterGroupName.equals("") ) {
            Object[] groupName = stateTable.keySet().toArray();
            for ( int i=0; i < groupName.length; i++) {
                strBuff.append( getGroupConnectionInfo( (String)groupName[i] ));
            }
        } else {
            return getGroupConnectionInfo( adapterGroupName );
        }
        return strBuff.toString();
    }

    // Adapter Connection Summary정보 리턴
    // 요약정보의 Start/Stop 정보를 표현한다.
    public String getConnectionSummary() {
        StringBuffer strBuff = new StringBuffer();
        AdapterManager  adapterMgr = AdapterManager.getInstance();

        // AdapterManager에서 전체 Adapter Group목록을 조회한다.
        List<String> allGroupList = adapterMgr.getAllAdapterGroupNames();
        if ( allGroupList == null ) {
            return CommonLib.getMessage("BICEAIMSA038", new String[]{"어뎁터그룹정보가 없습니다."});
        }

        ArrayList<String> netGroupList = new ArrayList<String>();
        // 전체 Adapter Group에서 NET Adapter의 Group만을 추출한다....
        for ( int i=0; i < allGroupList.size(); i++ ) {
            String netGroup = allGroupList.get( i );
            if ( adapterMgr.getAdapterGroupVO( netGroup ).getType().equals(com.eactive.eai.adapter.Keys.TYPE_NET) ) {
                netGroupList.add( netGroup );
            }
        }
        if ( netGroupList.size() < 1 ) {
            return CommonLib.getMessage("BICEAIMSA038", new String[]{"소켓 어뎁터그룹정보가 없습니다."});
        }
        Collections.sort( netGroupList );

        Object[] groupName = netGroupList.toArray();

        for ( int i=0; i < groupName.length; i++) {
            String grpInfo = getGroupConnectionInfo( (String)groupName[i] );

            AdapterGroupVO  adapterGroup = adapterMgr.getAdapterGroupVO( (String)groupName[i] );
            if ( adapterGroup == null ) continue;
            String[] adapterNames = adapterGroup.getAdapterNames();
            if ( adapterNames == null ) continue;

            // Connection 목록이 없는 경우
            if ( grpInfo.indexOf("BECEAIMSA006") >= 0 || grpInfo.indexOf("BWCEAIMSA005") >= 0 ) {
                for ( int j=0; j < adapterNames.length; j++ ) {
                    int maxConn = 0;
                    try {
                        //maxConn = createContext( adapterGroup.getAdapterVO( adapterNames[j] )).getMaxConnection();
                        maxConn = this.getContext( adapterNames[j] ).getMaxConnection();
                    } catch( Exception e ) {
                        continue;
                    }
                    strBuff.append( (String)groupName[i] ).append(",").append( adapterNames[j] ).append(",");
                    if ( maxConn == 0 ) {
                        strBuff.append( "UNLIMITED" );
                    } else {
                        strBuff.append( maxConn );
                    }
                    strBuff.append( ",0,0,0,");
                    strBuff.append( adapterGroup.getAdapterVO(adapterNames[j]).isStarted() );
                    strBuff.append("\n");
                }
                continue;
            }

            // Connection List를 갖고 있는 경우.
            // some action ....

            // 그룹명을 포함한 Connection List
            String[] gInfoList = grpInfo.split("\n");
            // 그룹명을 제외한 Connection List
            String[] aInfoList = new String[ gInfoList.length ];

            for ( int j=0; j < gInfoList.length; j++ ) {
                try {
                    aInfoList[ j ] = gInfoList[j].split(",", 2)[1];
                } catch( Exception e ) {}
            }

            // Adapter명별로 정렬
            Arrays.sort( aInfoList );

            String          tmpName  = "";
            StringBuffer    nameBuff = new StringBuffer();
            int             sendCount = 0;
            int             recvCount = 0;
            int             connCount = 0;

            for ( int j =0; j < gInfoList.length; j++ ) {
                if ( aInfoList[j] == null || aInfoList[j].equals("") ) continue;

                String [] aInfoBySocket = aInfoList[ j ].split(",");
                if ( aInfoBySocket == null || aInfoBySocket.length < 12 ) continue;

                if ( !tmpName.equals( aInfoBySocket[0] ) && !tmpName.equals("") ) {
                    // 요약정보 생성
                    int maxConn = 0;
                    try {
                        //maxConn = createContext( adapterGroup.getAdapterVO( tmpName )).getMaxConnection();
                        maxConn = this.getContext( tmpName ).getMaxConnection();
                    } catch( Exception e ) {}

                    strBuff.append( groupName[i] ).append(",");
                    strBuff.append( tmpName ).append(",");
                    if ( maxConn == 0 ) {
                        strBuff.append( "UNLIMITED" ).append(",");
                    } else {
                        strBuff.append( maxConn ).append(",");
                    }
                    strBuff.append( connCount ).append(",");
                    strBuff.append( sendCount ).append(",");
                    strBuff.append( recvCount ).append(",");
                    strBuff.append( "true" ).append("\n");

                    nameBuff.append( tmpName ).append(",");
                    // 임시 값 초기화
                    tmpName = aInfoBySocket[0];
                    sendCount = 0;
                    recvCount = 0;
                    connCount = 0;
                }

                tmpName = aInfoBySocket[0];

                try {
                    sendCount += Integer.parseInt( aInfoBySocket[7] );
                    recvCount += Integer.parseInt( aInfoBySocket[8] );
                } catch( Exception e ) {}

                if ( aInfoBySocket[9].equals("true") ) {
                    connCount += 1;
                }
            }
            // 최종Adapter에 대한 정보
            if ( !tmpName.equals("") ) {
                int maxConn = 0;
                try {
                    // maxConn = createContext( adapterGroup.getAdapterVO( tmpName )).getMaxConnection();
                    maxConn = this.getContext( tmpName ).getMaxConnection();
                } catch( Exception e ) {}

                strBuff.append( groupName[i] ).append(",");
                strBuff.append( tmpName ).append(",");
                if ( maxConn == 0 ) {
                    strBuff.append( "UNLIMITED" ).append(",");
                } else {
                    strBuff.append( maxConn ).append(",");
                }
                strBuff.append( connCount ).append(",");
                strBuff.append( sendCount ).append(",");
                strBuff.append( recvCount ).append(",");
                strBuff.append( "true" ).append("\n");
                nameBuff.append( tmpName );
            }

            // Adapter Group List에는 있으나, Connection List에 없는 Adapter정보
            for ( int k=0; k < adapterNames.length; k++ ) {
                if ( nameBuff.toString().indexOf( adapterNames[k] ) >= 0 ) continue;
                //
                int maxConn = 0;
                try {
                    maxConn = createContext( adapterGroup.getAdapterVO( adapterNames[k] )).getMaxConnection();
                } catch( Exception e ) {
                    continue;
                }
                strBuff.append( (String)groupName[i] ).append(",").append( adapterNames[k] ).append(",");
                if ( maxConn == 0 ) {
                    strBuff.append( "UNLIMITED" );
                } else {
                    strBuff.append( maxConn );
                }
                strBuff.append( ",0,0,0,");
                strBuff.append( adapterGroup.getAdapterVO(adapterNames[k]).isStarted() );
                strBuff.append("\n");
            }
        }

        return strBuff.toString();
    }

    private String getGroupConnectionInfo(String adapterGroupName) {
        StringBuffer strBuff = new StringBuffer();

        LinkedList<SocketService> list = stateTable.get( adapterGroupName );
		if ( list == null ) {
            strBuff.append( CommonLib.getMessage("BECEAIMSA006", new String[]{ adapterGroupName } )).append("\n");
            return strBuff.toString();
        }
		for (SocketService cInfo : list) {
			strBuff.append( cInfo.toString() ).append("\n");
		}
		
//        Object[] x = list.toArray();
//        for( int i=0; i < x.length; i++ ) {
//            SocketService cInfo = (SocketService) x[i];
//        }

        if ( list.size() == 0 ) {
            strBuff.append( CommonLib.getMessage("BWCEAIMSA005", new String[]{ adapterGroupName } )).append("\n");
        }

        return strBuff.toString();
    }
    public boolean isActiveAdapter(String adapterName) {
        AdminTable adminTable = (AdminTable) adminTableMap.get( adapterName );
        if ( adminTable == null ) return false;
        return  adminTable.getContext().isStarted();
    }

    public String getSessionInfo(String adapterGroupName ) {
        if ( adapterGroupName == null || adapterGroupName.equals("") ) {
            return SessionManager.getInstance().getSessionInfo();
        } else {
            return SessionManager.getInstance().getSessionInfo( adapterGroupName );
        }
    }
    // STOP A SOCKET ADAPTER CONFIGURATION
    public void stop( AdapterVO config ) throws Exception {
        String adapterName = config.getName();      // AdapterName
        Object[] controlList = getAdminList( adapterName );

        if ( controlList == null ) {
            String errMsg = CommonLib.getMessage("BWCEAIMSA004", new String[]{ config.getAdapterGroupName(), config.getName()});
            throw new Exception( errMsg );
        }

        AdminTable adminTable = (AdminTable) adminTableMap.get( adapterName );
        adminTable.getContext().setStarted( false );

        for ( int i=0; i < controlList.length; i++ ) {
            SocketService service = (SocketService) controlList[i];
            removeConnection( service );
            adminTable.removeConnection( service );
            service.shutdown();
         }

        adminTable.initialize();
        //adminTableMap.remove( adminTable );
    }


    public void stop() {
        if ( !isStarted() ) return;
        if(logger.isInfoEnabled()){
            logger.info( CommonLib.getMessage("BICEAIMSA022") );
        }

        this.started = false;

        // CONNECTION MANAGER STOP
        if(logger.isInfoEnabled()){
            logger.info( CommonLib.getMessage("BICEAIMSA016") );
        }
        this.connTimer.cancel();
        this.connTimer      = null;
        this.connManager    = null;
        if(logger.isInfoEnabled()){
            logger.info( CommonLib.getMessage("BICEAIMSA017") );
        }

        // RECOVERY MANAGER STOP
        if(logger.isInfoEnabled()){
            logger.info( CommonLib.getMessage("BICEAIMSA018") );
        }
        this.recoverTimer.cancel();
        this.recoverTimer      = null;
        this.recoverManager    = null;
        if(logger.isInfoEnabled()){
            logger.info( CommonLib.getMessage("BICEAIMSA019") );
        }

        // SESSION MANAGER STOP
        if(logger.isInfoEnabled()){
            logger.info( CommonLib.getMessage("BICEAIMSA020") );
        }

        // SESSION TABLE 및 IDLE TABLE에서 해당 THREAD모두 STOP
        this.removeAllSession();
        this.sessionManager.stop();
        if(logger.isInfoEnabled()){
            logger.info( CommonLib.getMessage("BICEAIMSA021") );
        }

        OutboundChannelManager.getInstance().removeAll();

        controlMap.clear();

        if ( getAllAdminList().length < 1 ) {
            stateTable.clear();
            if(logger.isInfoEnabled()){
                logger.info( CommonLib.getMessage("BICEAIMSA023") );
            }
            return;
        }

        if(logger.isInfoEnabled()){
            logger.info( CommonLib.getMessage("BICEAIMSA024") );
        }

        Object[] dList = getAllAdminList();
        for ( int i=0; i < dList.length; i++) {
            SocketService aService = (SocketService)dList[i];
            aService.shutdown();
            AdminTable adminTable = (AdminTable) adminTableMap.get( aService.getContext().getAdapterName() );
            if ( adminTable != null ) {
                adminTable.removeConnection( aService );
            }
        }
        if(logger.isInfoEnabled()){
            logger.info( CommonLib.getMessage("BICEAIMSA025") );

            logger.info( CommonLib.getMessage("BICEAIMSA023") );
        }
    }

	public synchronized void addConnection( SocketService service ) {
        ConfigurationContext  context = service.getContext();
        String	key = context.getAdapterGroupName();
        String  adapterName = context.getAdapterName();
        // Outbound Socket Connection의 경우...
        // OutboundChannelManager 에 Connection을 추가시킨다.
        // if ( context.getBoundUsage().equals( ConfigurationContext.OUTBOUND_SOCKET ) ) {

        LinkedList<SocketService> list = stateTable.get( key );
        list.add( service );

        if ( context.getBoundUsage().equals( ConfigurationContext.OUTBOUND_SOCKET ) ) {
            OutboundChannelManager.getInstance().addConnection(key, service);
        }

        if ( context.getSocketType().equals( ConfigurationContext.CLIENT_SOCKET ) ) {
            AdminTable adminTable = (AdminTable) adminTableMap.get( adapterName );
            if ( adminTable != null ) {
                adminTable.checkRecoverStatus();
            }
        }

        // RemoteIP:Port관리
        if ( context.getSocketType().equals( ConfigurationContext.SERVER_SOCKET ) ) {
            try {
                String  remoteInfo = new StringBuffer().append( service.getCurrentSocket().getInetAddress().getHostAddress()).append(":").append( service.getCurrentSocket().getPort()).toString();
                controlMap.put( remoteInfo, service );
            } catch ( Exception e ) {}
        }
	}

	public synchronized void removeConnection( SocketService service ) {
        ConfigurationContext  context = service.getContext();
        String	key = context.getAdapterGroupName();

        if ( context.getBoundUsage().equals( ConfigurationContext.OUTBOUND_SOCKET ) ) {
           OutboundChannelManager.getInstance().removeConnection(key, service);
        }

        LinkedList<SocketService> list = stateTable.get( key );
        list.remove( service );

        if ( context.getSocketType().equals( ConfigurationContext.CLIENT_SOCKET ) ) {
            AdminTable adminTable = (AdminTable) adminTableMap.get( context.getAdapterName() );
            if ( adminTable != null ) {
                adminTable.removeConnection( service );
            }
        }

        // RemoteIP:Port관리
        if ( context.getSocketType().equals( ConfigurationContext.SERVER_SOCKET ) ) {
            try {
                if ( controlMap.containsValue( service ) ) {
                    Object[] x = controlMap.keySet().toArray();
                    for ( int i = 0; x != null && i < x.length; i++ ) {
                        String remoteInfo = (String)x[i];
                        Object y = controlMap.get( remoteInfo );
                        if ( y == null ) continue;
                        if ( ((SocketService)y) == service ) {
                            controlMap.remove( remoteInfo );
                            break;
                        }
                    }
                }
            } catch ( Exception e ) {}
        }

	}

    public synchronized String disconnect(String remoteIP, String port) {
        StringBuffer strBuff    = new StringBuffer();
        String  remoteInfo      = new StringBuffer().append( remoteIP ).append(":").append( port ).toString();

        Object x = controlMap.get( remoteInfo );
        try {
            if ( x != null ) {
                SocketService svc = (SocketService)x;
                StringBuffer connInfo = new StringBuffer();
                connInfo.append("Local Address=");
                connInfo.append( svc.getCurrentSocket().getLocalAddress().getHostAddress()).append(":").append(svc.getCurrentSocket().getLocalPort()).append(", ");
                connInfo.append("Remote Address=");
                connInfo.append( svc.getCurrentSocket().getInetAddress().getHostAddress()).append(":").append(svc.getCurrentSocket().getPort());

                svc.shutdown();
                strBuff.append( CommonLib.getMessage("BICEAIMSA026",new String[]{ connInfo.toString() }));
            } else {
                strBuff.append( CommonLib.getMessage("BECEAIMSA007",new String[]{ remoteInfo }));
            }
        } catch ( Exception e ) {
            strBuff.append( CommonLib.getMessage("BECEAIMSA007",new String[]{ remoteInfo }));
        }
        return strBuff.toString();
    }

  /*
   *    SessionService 스레드가 시작할때 이걸 호출함.
   */
	public synchronized void addSession( SessionService session ) {
        String	key = session.getAdapterGroupName();
        this.sessionManager.addSession( key, session );
        LinkedList<SessionService> list = sessionTable.get( key );
        list.add( session );
	}

    @SuppressWarnings("rawtypes")
	public void removeAllSession() {
        // SESSION TABLE에서 해당 SESSION THREAD STOPPING
        Object[] it     = this.sessionTable.values().toArray();
        if ( it == null ) {
            return;
        }

        SessionService  session     = null;
        LinkedList      sessionList = null;
        for ( int i=0; i < it.length; i++ ) {
            sessionList = (LinkedList) it[i];

            Object[] sit = sessionList.toArray();
            for(int j=0; j < sit.length; j++ ) {
                session = (SessionService)sit[j];
                try {
                    session.shutdown();
                } catch(Exception e) {}
            }
        }
    }

    @SuppressWarnings("rawtypes")
	public String getSessionStatusInfo() {
        Object[] it     = this.sessionTable.values().toArray();

        if ( it == null ) {
            return CommonLib.getMessage("BICEAIMSA038", new String[]{"세션정보가 없습니다."});
        }

        StringBuffer strBuff = new StringBuffer();
        strBuff.append("\n");
        SessionService  session     = null;
        LinkedList      sessionList = null;
        for ( int i=0; i < it.length; i++ ) {
            sessionList = (LinkedList) it[i];

            Object[] sit = sessionList.toArray();
            for(int j=0; j < sit.length; j++ ) {
                session = (SessionService)sit[j];
                try {
                    strBuff.append( session.toString() ).append("\n");
                } catch(Exception e) {}
            }
        }

        return strBuff.toString();

    }

	public synchronized void removeSession( SessionService session ) {
		String	key = session.getAdapterGroupName();
		this.sessionManager.removeSession(key, session);
		LinkedList<SessionService> list = sessionTable.get( key );
		list.remove( session );

	}
	public void removeError(SocketService service) {
        if ( errorTable != null ) {
            this.errorTable.remove( service );
        }
    }

	public void removeState(SocketService service) {
        if ( stateTable != null ) {
            String	key = service.getContext().getAdapterGroupName();

            if ( service.getContext().getBoundUsage().equals( ConfigurationContext.OUTBOUND_SOCKET) ) {
                OutboundChannelManager.getInstance().removeConnection(key, service);
            }

            LinkedList<SocketService> list = stateTable.get( key );
            list.remove( service );
            this.errorTable.remove( service );

        }
    }

    public boolean checkActiveStatus(String adapterGroupName, String adapterName) {
        boolean  good = false;
        LinkedList<SocketService> list = stateTable.get( adapterGroupName );
        if ( list == null || list.size() < 1 ) return false;
        for ( int i = 0; i < list.size(); i++ ) {
            try {
                SocketService aSvc = (SocketService) list.get( i );
                if ( aSvc.getContext().getAdapterName().equals( adapterName ) ) {
                    good = true;
                    break;
                }
            } catch ( Exception e ) {}
        }

        return good;
    }

	public synchronized boolean addError( SocketService service ) {
        if ( started ) {
            //if ( service.getContext().getBoundUsage().equals( ConfigurationContext.OUTBOUND_SOCKET) ) {
            if ( service.getContext().getSocketType().equals( ConfigurationContext.CLIENT_SOCKET ) ) {
                String	key = service.getContext().getAdapterGroupName();
                String  adapterName = service.getContext().getAdapterName();

                if ( service.getContext().getBoundUsage().equals( ConfigurationContext.OUTBOUND_SOCKET) ) {
                    OutboundChannelManager.getInstance().removeConnection(key, service);
                }

                LinkedList<SocketService> list = stateTable.get( key );
        		list.remove( service );
                this.errorTable.remove( service );

                AdminTable adminTable = (AdminTable) adminTableMap.get( adapterName );
                if ( adminTable != null ) {
                    adminTable.checkErrorStatus();
                }
            }

            return this.errorTable.add( service );
        } else {
            return false;
        }
	}

    public boolean isStarted() {
        return this.started;
    }

    /**
     * Socket Adapter Administation Command Provider....
     */
    public String executeCommand( String command ) {

        String     commands = "stop,start,get,set,disconnect";
        String     options  = "connection,session,trace,config";
        String     inCmd    =  command ;

        if ( inCmd == null || inCmd.equals("") ) {
            return CommonLib.getMessage("BECEAIMSA011", new String[]{ command });
        }

        String[]    cList = inCmd.split(" ");
        if ( cList.length < 3 ) {
            return CommonLib.getMessage("BECEAIMSA011", new String[]{ command });
        }
        String cmd = cList[0].toLowerCase();
        String option = cList[1].toLowerCase();

        if ( commands.indexOf( cmd ) < 0 ) {
            return CommonLib.getMessage("BECEAIMSA011", new String[]{ command });
        } else if ( (cmd.equals("get") || cmd.equals("set"))
                    && options.indexOf( option ) < 0 ) {
            return CommonLib.getMessage("BECEAIMSA012", new String[]{ command });
        }

        String  rtnMsg = " ";
        if ( cmd.equals("stop") ) {
            rtnMsg = this.stop( cList[1], cList[2] );
        } else if ( cmd.equals("start") ) {
            rtnMsg = this.start( cList[1], cList[2] );
        } else if ( cmd.equals("get") ) {
            // Connections
            if ( option.equals("connection") ) {
                if ( cList[2].equalsIgnoreCase("all") ) {
                    rtnMsg = this.getConnectionInfo( null );
                } else if ( cList[2].equalsIgnoreCase("summary") ) {
                    rtnMsg = this.getConnectionSummary();
                } else {
                    rtnMsg = this.getConnectionInfo( cList[2] );
                }
            // Config
            } else if ( option.equals("config") ) {
                ConfigurationContext ctx = this.getContext( cList[2] );
                if ( ctx == null ) {
                    rtnMsg = CommonLib.getMessage("BECEAIMSA028", new String[] { cList[2] } );
                } else {
                    rtnMsg = this.getContext( cList[2] ).toString();
                }
            // Sessions
            } else {
                if ( cList[2].equalsIgnoreCase("all") ) {
                    rtnMsg = this.getSessionInfo( null );
                } else {
                    rtnMsg = this.getSessionInfo( cList[2] );
                }
            }
        } else if ( cmd.equals("set") ) {
            if ( cList.length < 4 ) {
                return CommonLib.getMessage("BECEAIMSA012", new String[]{ command });
            }
            // Connections
            int newCount = 0;
            try {
                newCount = Integer.parseInt( cList[3] );
            } catch ( Exception e ) {
                return CommonLib.getMessage("BECEAIMSA012", new String[]{ command });
            }

            if ( option.equals("connection") ) {
                rtnMsg = this.adjustConnection( cList[2], newCount );
            // Sessions
            } else if ( option.equals("session") ) {
                try {
                    rtnMsg = this.adjustSession( cList[2], newCount );
                } catch( Exception e ) {
                    rtnMsg = CommonLib.getMessage("BECEAIMSA013",  new String[]{ cList[2], e.getMessage() } );
                }
            // trace level 조정
            } else {
                ConfigurationContext ctx = this.getContext( cList[2] );
                if ( newCount < 0 || newCount > 2 ) {
                    rtnMsg = CommonLib.getMessage("BECEAIMSA014");
                }
                if ( ctx == null ) {
                    rtnMsg = CommonLib.getMessage("BECEAIMSA015", new String[]{ cList[2] } );
                    return rtnMsg;// 20250910 npe 첨부
                }
                ctx.setTraceLevel( newCount );
                String newLevel = "";

                if ( newCount == 0 ) newLevel = "0(ERROR)";
                else if ( newCount == 1 ) newLevel = "1(INFO)";
                else newLevel = "2(DEBUG)";

                rtnMsg = CommonLib.getMessage("BICEAIMSA037", new String[]{ cList[2], newLevel });
            }
        } else if ( cmd.equals("disconnect") ) {
            if ( cList.length < 3 ) {
                return CommonLib.getMessage("BECEAIMSA012", new String[]{ command });
            }

            rtnMsg = this.disconnect( cList[1], cList[2] );
        } else {
            return CommonLib.getMessage("BECEAIMSA011", new String[]{ command });
        }

        return rtnMsg;
    }

    public String toString() {
        StringBuffer sb = new StringBuffer();

        sb.append("\n■ stateTable : cnt="+ stateTable.size());
        for(Iterator<String> iter = stateTable.keySet().iterator(); iter.hasNext();) {
            Object key = iter.next();
            sb.append("\n    ▣ " + key + " : " + (stateTable.get(key)).size());
            for(Iterator<SocketService> iter2 = (stateTable.get(key)).iterator(); iter2.hasNext();) {
                sb.append("\n        ▷ " + iter2.next() + " : ");
            }
        }
        sb.append("\n■ errorTable : " + errorTable);
        sb.append("\n■ sessionTable : cnt="+ sessionTable.size());
        for(Iterator<String> iter = sessionTable.keySet().iterator(); iter.hasNext();) {
            Object key = iter.next();
            sb.append("\n    ▣ " + key + " : cnt=" + (sessionTable.get(key)).size());
            for(Iterator<SessionService> iter2 = (sessionTable.get(key)).iterator(); iter2.hasNext();) {
                sb.append("\n        ▷ " + iter2.next() + " : ");
            }
        }

        sb.append(connManager);
        sb.append(recoverManager);
        sb.append(sessionManager);
        sb.append(OutboundChannelManager.getInstance().toString());

        sb.append("\n■ adminTableMap : cnt="+ adminTableMap.size());
        for(Iterator<String> iter = adminTableMap.keySet().iterator(); iter.hasNext();) {
            Object key = iter.next();
            sb.append("\n    ▣ " + key + " : " + adminTableMap.get(key));
        }
        sb.append("\n■ controlMap : cnt="+ controlMap.size());
        for(Iterator<String> iter = controlMap.keySet().iterator(); iter.hasNext();) {
            Object key = iter.next();
            sb.append("\n    ▣ " + key + " : " + controlMap.get(key));
        }

        return sb.toString();
    }

    public SocketService getSocketService(String adapterGroupName, String adapterName)
    {
    	Object[] services = this.getAllAdminList();
        for (int i=0; i< services.length; i++) {
            SocketService ss = (SocketService)services[i];
            ConfigurationContext ctx = ss.getContext();

            if (ctx.getAdapterGroupName().equalsIgnoreCase(adapterGroupName) &&
                ctx.getAdapterName().equalsIgnoreCase(adapterName))
            {
                return ss;
            }
        }
        return null;
    }

    public String getAllServerSocketInformation()
    {
        logger.info("SocketAdapterManager.getAllServerSocketInformation called");
        StringBuffer    sb = new StringBuffer();
        Object[] services = this.getAllAdminList();
        logger.info("SocketAdapterManager.getAllServerSocketInformation: AdminTable count = " + services.length);
        for (int i=0; i< services.length; i++) {

            SocketService ss = (SocketService)services[i];
            ConfigurationContext ctx = ss.getContext();
            if (ss.isActive()) {
                sb.append("[ACTIVE]");
            } else {
                sb.append("[INACTIVE]");
            }
            sb.append("[" + ctx.getAdapterGroupName() + "]");
            sb.append("[" + ctx.getAdapterName() + "]");
            sb.append(" USAGE: SERVER SOCKET ");
            sb.append(" PORT : (" + ctx.getLocalPortNumber() + ")");
            sb.append(" <BR>\n");


        }
        return sb.toString();
    }

	public int getSocketServiceCount(String adapterGroupName, String adapterName) {
		int cnt = 0;

		LinkedList<SocketService> serviceList = stateTable.get(adapterGroupName);
		for (int i=0; i<serviceList.size(); i++) {
			SocketService service = (SocketService) serviceList.get(i);
			if (service.getContext().getAdapterName().equals(adapterName)) {
				cnt++;
			}
		}
		return cnt;
	}
}
