package gov.va.med.authentication.kernel;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
 
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.log4j.Logger;
import org.apache.log4j.Level;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

/**
 * Initialization servlet to initialize KAAJEE. Should be configured to launch at application startup, for
 * an application hosting KAAJEE. Requires a parameter to pass the location of the KAAJEE configuration file.
 * Example web.xml configuration of this servlet:
 * <pre>
 *    &lt;servlet&gt;
 *     &lt;servlet-name&gt;KaajeeInit&lt;/servlet-name&gt;
 *     &lt;servlet-class&gt;gov.va.med.authentication.kernel.InitKaajeeServlet&lt;/servlet-class&gt;
 *     &lt;init-param&gt;
 *       &lt;param-name&gt;kaajee-config-file-location&lt;/param-name&gt;
 *       &lt;param-value&gt;/WEB-INF/kaajeeConfig.xml&lt;/param-value&gt;
 *     &lt;/init-param&gt;
 *     &lt;load-on-startup&gt;3&lt;/load-on-startup&gt;
 *   &lt;/servlet&gt;  
 * </pre>
 * @author VHIT Security and Other Common Services (S&OCS)
 * @version 1.1.0.007
 */
public class InitKaajeeServlet extends HttpServlet {

	private static Logger logger = Logger.getLogger(InitKaajeeServlet.class);

	/**
	 * servlet parameter name to pass in the location of the KAAJEE configuration file
	 */
	public static final String CONFIG_FILE_PARAM_NAME = "kaajee-config-file-location";
	/**
	 * Runs the KAAJEE startup controller.
	 * @throws ServletException 
	 */
	public void init() throws ServletException {

		//Check for existence of Kaajee Authenticator
		CheckForAuthenticator();
		
		InputStream configFileStream = null;

		// retrieve KAAJEE configuration settings
		String configFileLocation = getInitParameter(CONFIG_FILE_PARAM_NAME);
		if (logger.isEnabledFor(Level.DEBUG)) {
			logger.debug("Config File Location: " + configFileLocation);
		}

		// if init-file is not set, then no point in trying
		if (configFileLocation == null) {
			if (logger.isEnabledFor(Level.ERROR)) {
				StringBuffer sb = new StringBuffer("KAAJEE configuration file location '");
				sb.append(configFileLocation);
				sb.append("' is null; can't retrieve KAAJEE settings.");
				logger.error(sb.toString());
			}
		} else {

			if (logger.isEnabledFor(Level.DEBUG)) {
				StringBuffer sb = new StringBuffer("about to try to retrieve '");
				sb.append(configFileLocation);
				sb.append("'");
				logger.debug(sb.toString());
			}
			try {
				// read in the XML doc
				configFileStream = getServletContext().getResourceAsStream(configFileLocation);
				DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
				DocumentBuilder builder = factory.newDocumentBuilder();
				Document configDocument = builder.parse(configFileStream);

				// pass to the startup controller
				StartupController.doStartup(configDocument);

			} catch (FactoryConfigurationError e) {
				logger.error("Problem reading configuration file.", e);
			} catch (ParserConfigurationException e) {
				logger.error("Problem reading configuration file.", e);
			} catch (SAXException e) {
				logger.error("Problem reading configuration file.", e);
			} catch (IOException e) {
				logger.error("Problem reading configuration file.", e);
			} finally {

				// close the stream
				try {
					configFileStream.close();
				} catch (IOException e) {
					if (logger.isEnabledFor(Level.ERROR)) {
						logger.error("Problem closing input stream for KAAJEE configuration file.", e);
					}
				}
			}
		}
	}

	/**
	 * implements required doGet method.
	 * @param request HttpServletRequest object
	 * @param response HttpServletResponse object
	 * @throws ServletException if problem encountered.
	 */
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException {
		CheckForAuthenticator();
		displayInfo(response);
	}

	/**
	 * implements required doPost method.
	 * @param request HttpServletRequest object
	 * @param response HttpServletResponse object
	 * @throws ServletException if problem encountered.
	 */
	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException {
		CheckForAuthenticator();
		displayInfo(response);
	}

	/**
	 * what to display if someone runs the servlet interactively
	 * @param response
	 */
	private void displayInfo(HttpServletResponse response) {
		try {
			PrintWriter out = response.getWriter();
			out.println("InitKaajeeServlet");
		} catch (IOException e) {
			if (logger.isEnabledFor(Level.ERROR)) {
				logger.error("Could not get a PrintWriter to display output.", e);
			}
		}
	}
	
	/**
	 * check for existence of KaajeeManageableAuthenticator needed by KAAJEE
	 * @throws ServletException if problem encountered.
	 */
	private void CheckForAuthenticator() 
    throws ServletException {

    	Context ctx = null;
    	ObjectName authenticator = null;
    	try
    	{
    		ctx           = new InitialContext();
    		MBeanServer mbs = (MBeanServer) ctx.lookup("java:comp/env/jmx/runtime");
    		authenticator = LoginControllerUtils.getKaajeeAuthenticator(mbs);
    	}
    	catch (Exception ex)
    	{
    		logger.error("Error finding KaajeeManageableAuthenticator in MBeanServer", ex);
    		throw new ServletException("Error finding KaajeeManageableAuthenticator in MBeanServer");
    	}finally {
    		try {
    			ctx.close();
    		} catch (NamingException e) {
    			logger.error("Error closing context: ", e);
    		}
    	}          
		if (authenticator == null) 
			throw new ServletException("Error, KaajeeManageableAuthenticator doesn't exist in security realm");
	}

}
