/***********************************************************
Copyright (C) 2004 VeriSign, Inc.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

http://www.verisign.com/nds/naming/namestore/techdocs.html
***********************************************************/
package com.verisign.epp.transport;

import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.security.cert.X509Certificate;

import javax.net.ssl.HandshakeCompletedEvent;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSocket;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Server thread for processing a new client connection.
 */
public class EPPServerThread extends Thread {
	/** Category for logging */
	private static Logger cat = LoggerFactory.getLogger(EPPServerThread.class);

	/** Client socket associated with thread */
	private Socket socket = null;

	/** Input stream of client socket */
	private InputStream inputStream = null;

	/** Output stream of client socket */
	private OutputStream outputStream = null;

	/** Client connection handler */
	private ServerEventHandler handler = null;

	/**
	 * Creates a new EPPServerThread object.
	 *
	 * @param aSocket
	 *           Client socket to handle in thread
	 * @param aHandler
	 *           Client connection handler
	 *
	 * @throws IOException
	 *            Exception process the client socket
	 */
	public EPPServerThread(Socket aSocket, ServerEventHandler aHandler) throws IOException {
		super("EPPMultiServerThread");
		this.socket = aSocket;
		this.handler = aHandler;

		// SSLSocket?
		if (aSocket instanceof SSLSocket) {

			// Register handshake completion listener
			((SSLSocket) aSocket).addHandshakeCompletedListener(new HandshakeCompletedListener() {
				@Override
				public void handshakeCompleted(HandshakeCompletedEvent aEvent) {
					try {
						X509Certificate thePeerCert = (X509Certificate) aEvent.getPeerCertificates()[0];
						X509Certificate theRootCert = (X509Certificate) aEvent
						      .getPeerCertificates()[aEvent.getPeerCertificates().length - 1];
						cat.debug("Server SSL Handshake" + ": Cipher = " + aEvent.getCipherSuite() + ": Protocol = "
						      + aEvent.getSession().getProtocol() + ": Peer (name, serial #, issuer) = ("
						      + thePeerCert.getIssuerX500Principal().getName() + "," + thePeerCert.getSerialNumber() + ","
						      + thePeerCert.getIssuerX500Principal().getName() + ")" + ": Root (name, serial #) = ("
						      + thePeerCert.getIssuerX500Principal().getName() + "," + thePeerCert.getSerialNumber() + ","
						      + thePeerCert.getIssuerX500Principal().getName() + ")");
					}
					catch (SSLPeerUnverifiedException e) {
						// ignore
					}
				}
			});
		}

		try {
			this.inputStream = this.socket.getInputStream();
			/**
			 * JG 2/16/05 - Ensured that the output stream is buffered so the EPP
			 * header and packet are sent in a single packet. 20480 bytes was
			 * chosen to ensure that all EPP packets can be held in the buffered
			 * output stream.
			 */
			this.outputStream = new BufferedOutputStream(this.socket.getOutputStream(), 20480);
		}
		catch (IOException myException) {
			myException.printStackTrace();
			throw new IOException(myException.getMessage());
		}
	}

	/**
	 * Process the client connection.
	 */
	@Override
	public void run() {
		cat.info("run(): enter");

		if (this.handler == null) {
			cat.error("The Server Handler is not Set");
			return;
		}

		try {
			ServerEventHandler myConHandler = (ServerEventHandler) this.handler.clone();
			myConHandler.handleConnection(this.inputStream, this.outputStream);
		}
		catch (CloneNotSupportedException myException) {
			cat.error("Server Run : CloneNotSupportedException : " + myException.getMessage(), myException);
			return;
		}

		cat.info("run(): Closing socket");

		try {
			this.socket.close();
		}
		catch (IOException myException) {
			cat.error("Server Run : IOException When Closing the Connection : " + myException.getMessage(), myException);

			return;
		}

		cat.info("run(): exit");
	}
}
