package com.interpress_project.modernshare.svnserver;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Properties;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;

import com.interpress_project.modernshare.ipcommon.SystemBase;
import com.interpress_project.modernshare.model.PropertyManager;

/**
 * @stereotype Manager
 */
public class ServerManager extends Thread {

	/**
	 * @stereotype creates
	 */
	/*#com.interpress_project.modernshare.svnserver.RequestThread Dependency_Link*/

	private final SystemBase sb = SystemBase.getInstance();
	private InetAddress hostaddr;
	private SSLServerSocket serverSocket;
	private PropertyManager propmgr;
	private boolean bShutdown = false;
	private int listen_port = 0, tcpbufsz = 0;
	private ThreadPoolExecutor requestThreadPool;

	/**
	 * ServerManager
	 */
	public ServerManager() {
		propmgr = PropertyManager.getInstance();

		int THREAD_INITIALCOUNT = propmgr.getSVNServerThreadMinimum();
		int THREAD_MAXCOUNT = propmgr.getSVNServerThreadMaximum();

		CreateServerSocket();

		LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
		requestThreadPool = new ThreadPoolExecutor(THREAD_INITIALCOUNT, THREAD_MAXCOUNT, 32, TimeUnit.SECONDS, queue);

		sb.getLogger().debug("ServerManager started.");
	}

	/**
	 * CreateServerSocket
	 */
	private void CreateServerSocket() {
		listen_port = propmgr.getSVNServerPort();
		tcpbufsz = propmgr.getTcpBufSz();

		Properties props = System.getProperties();
		props.setProperty("javax.net.ssl.keyStore", propmgr.getKeystore());
		props.setProperty("javax.net.ssl.keyStorePassword", propmgr.getKeystorePass());
		//props.setProperty("javax.net.debug", "ssl");

		try {
			hostaddr = InetAddress.getByName(propmgr.getServerName());
		}
		catch (UnknownHostException ex) {
			sb.getLogger().fatal(
			  "Fail to resolve the hostname,  " + propmgr.getServerName() + ". Server is forcely terminated.", ex);
			System.exit(-1);
		}

		try {
			/**
			 * アプリケーションが RFC1323 で定義されている 64K バイトを超える受信ウィンドウを
			 * 使用可能にする必要がある場合には、 ローカルアドレスにバインドする前に 推奨値を
			 * ServerSocket で設定する必要があります。 
			 */
			SSLServerSocketFactory sslserversocketfactory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
			serverSocket = (SSLServerSocket) sslserversocketfactory.createServerSocket();
			serverSocket.setReceiveBufferSize(tcpbufsz);
			serverSocket.setReuseAddress(true);
			serverSocket.setSoTimeout(12 * 1000);

			InetSocketAddress endpoint = new InetSocketAddress(hostaddr, listen_port);
			serverSocket.bind(endpoint, 64);
		}
		catch (IOException ex) {
			sb.getLogger().fatal("Fail to start up secure guard. Server is forcely terminated.", ex);
			System.exit(-1);
		}
	}

	/**
	 * run
	 */
	public void run() {
		sb.getLogger().info("SVN Server name : " + propmgr.getServerName());
		sb.getLogger().info("SVN Server port : " + propmgr.getSVNServerPort());

		try {
			tcpbufsz = serverSocket.getReceiveBufferSize();
			sb.getLogger().info("SVN buffer size : " + tcpbufsz + "(byte)");
		}
		catch (SocketException ex) {
		}

		sb.getLogger().info("SVN Server thread pool(min) : " + propmgr.getSVNServerThreadMinimum());
		sb.getLogger().info("SVN Server thread pool(max) : " + propmgr.getSVNServerThreadMaximum());
		sb.getLogger().info("Secure guard started.");

		int targetport = Integer.parseInt(propmgr.getSVNServerCommandPort());
		int timeout = 5 * 1000;
		int threadTimeout = propmgr.getThreadTimeout();

		while (!bShutdown) {
			try {
				Socket socket = serverSocket.accept();
				if (bShutdown) {
					break;
				}
				sb.getLogger().debug("Connection accepted...");
				requestThreadPool.execute(new RequestThread(socket, targetport, tcpbufsz, timeout, threadTimeout));
			}
			catch (Exception ex) {
			}
		}
		sb.getLogger().debug("Leave ServerManager mainloop().");
	}

	/**
	 * shutdown
	 */
	public void shutdown() {
		bShutdown = true;
		requestThreadPool.shutdownNow();

		try {
			Socket clientSocket = new Socket(hostaddr, listen_port);
			clientSocket.close();
		}
		catch (Exception ex) {
		}
		sb.getLogger().debug("ServerManager terminated.");
	}

	/**
	 * dispose
	 */
	public void dispose() {
		shutdown();
	}
}
