package com.eactive.eai.common.routing;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.rmi.server.RMISocketFactory;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang3.time.StopWatch;
import org.springframework.remoting.RemoteConnectFailureException;
import org.springframework.remoting.rmi.RmiProxyFactoryBean;

import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.routing.rmi.GWRemoteProxy;
import com.eactive.eai.common.routing.rmi.TimeoutRmiClientSocketFactory;
import com.eactive.eai.common.util.Logger;


public class GWRemoteProxyClient {
	
	static Logger logger = Logger.getLogger(Logger.LOGGER_SIFT);
	private static String SERVICE_URL = "%s/RemoteProxy"; // rmi://%s:1099
	
	private static ConcurrentHashMap<String, RmiProxyFactoryBean> factoryMap = new ConcurrentHashMap<String, RmiProxyFactoryBean>();

	private static RmiProxyFactoryBean getFactory(String remoteUrl) {
		RmiProxyFactoryBean factory = factoryMap.get(remoteUrl);
		return factory;
	}
	
	private synchronized static RmiProxyFactoryBean initFactory(String remoteUrl, int connectTimeout, int timeout) {
		RmiProxyFactoryBean factory = factoryMap.get(remoteUrl);
		if(factory != null) return factory;
		
		factory = new RmiProxyFactoryBean();
    	factory.setServiceInterface(GWRemoteProxy.class);
    	factory.setServiceUrl(String.format(SERVICE_URL, remoteUrl));
    	TimeoutRmiClientSocketFactory registryClientSocketFactory = new  TimeoutRmiClientSocketFactory();
    	
    	try {
    		RMISocketFactory f = RMISocketFactory.getSocketFactory();
    		if(f == null) {
				RMISocketFactory.setSocketFactory(new RMISocketFactory() { 
					public Socket createSocket(String host, int port) throws IOException { 
						 int timeout =  120 *1000; // 타임아웃을 기본 120초로 설정
						 Socket socket = new Socket(host, port); 
						 socket.setSoTimeout(timeout); 
						 socket.setSoLinger(false, 0); 
	//					 System.out.println("RMISocketFactory timeout >> " + timeout);
						 return socket; 
					} 
					public ServerSocket createServerSocket(int port) throws IOException { 
						return new ServerSocket(port); 
					} 
				}
				);
    		}
    		else {
    			logger.warn("initFactory RMISocketFactory already defined : " + f.toString());
    		}
	    	registryClientSocketFactory.setConnectTimeout(connectTimeout);
	    	registryClientSocketFactory.setTimeout(timeout);
	    	factory.setRegistryClientSocketFactory(registryClientSocketFactory);
	    	factory.setRefreshStubOnConnectFailure(true);
	    	factory.afterPropertiesSet();
	    	factoryMap.put(remoteUrl, factory);
	    	return factory;
		} catch (Exception e) {
			logger.error("initFactory Exception - " + remoteUrl, e);
			return null;
		}
	}
    
    public static Object callProxyBean(Object message, Properties prop) throws Exception {
    	Object response = null;
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        String remoteUrl = prop.getProperty(RouteKeys.REMOTE_CALL_URL);
        
        Properties properties = PropManager.getInstance().getProperties(RouteKeys.REMOTE_CALL);
        String connectionTimeout = properties.getProperty(RouteKeys.REMOTE_CALL_CONNECTION_TIMEOUT ,"1000");
//        String readTimeoutAsync = properties.getProperty(RouteKeys.REMOTE_CALL_READ_TIMEOUT_ASYNC ,"4000");
//        String readTimeoutSync = properties.getProperty(RouteKeys.REMOTE_CALL_READ_TIMEOUT_SYNC  ,"60000");
        
        int connectTimeout = Integer.parseInt(connectionTimeout);
        int timeout = 2000;
        
        if(logger.isDebug()) {
        	logger.debug("GWRemoteProxyClient] remoteUrl         - " + remoteUrl);        	
        	logger.debug("GWRemoteProxyClient] connectionTimeout - " + connectionTimeout);
        }
    	
//        if( EAIMessageKeys.ASYNC_SVC.equals(psvItfTp) ) {
//        	timeout = Integer.parseInt(readTimeoutAsync);
//        }
//        else {
//        	timeout = Integer.parseInt(readTimeoutSync);
//        }
        
        RmiProxyFactoryBean factory = null;
    	try {
        	if(logger.isDebug()) {
            	logger.debug("GWRemoteProxyClient] Get RemoteProxy/call Service");        	
            }
        	if(remoteUrl == null) {
        		throw new Exception("remoteUrl is NULL"); 
        	}
        	factory = getFactory(remoteUrl);
        	if(factory == null) {
        		factory = initFactory(remoteUrl, connectTimeout, timeout);
        		if(factory == null) {
        			logger.error("GWRemoteProxyClient] initFactory Error : " + remoteUrl);
        		}
        	}
    		GWRemoteProxy service = (GWRemoteProxy) factory.getObject();        	
        	response = service.callService(message, prop);
        } catch(RemoteConnectFailureException ie) {
        	logger.warn("GWRemoteProxyClient] Reset RMI Factory : "+ ie.getMessage());
        	factory = initFactory(remoteUrl, connectTimeout, timeout);
        	GWRemoteProxy service = (GWRemoteProxy) factory.getObject();        	
        	response = service.callService(message, prop);
        } catch(Exception e) {
        	logger.error("GWRemoteProxyClient] Get RemoteProxy/call Service Error : "+ e.getMessage(),e);
            throw e;   
        } catch(Throwable t) {
        	logger.error("GWRemoteProxyClient] Get RemoteProxy/call Service Error : "+ t.getMessage(),t);
            throw new Exception("GWRemoteProxyClient] Get RemoteProxy/call Service Error : "+ t.getMessage());
        } finally{
        }
        
        stopWatch.stop();

        return response;
    }
}
