package es.ejie.frmk.control.canal;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.ActionServlet;

import es.ejie.frmk.conectores.conectorJDBC.Q70ConectorJDBC;
import es.ejie.frmk.control.arranque.Q70GestorArranqueConfiguracion;
import es.ejie.frmk.control.excepciones.Q70GestorDeExcepciones;
import es.ejie.frmk.control.monitor.Q70MonitorTiemposAcceso;
import es.ejie.frmk.infraestructura.logs.navegacion.Q70LogNavegacion;
import es.ejie.frmk.infraestructura.traza.Q70TraceLevel;
import es.ejie.frmk.infraestructura.traza.Q70Traza;
import es.ejie.frmk.listeners.base.Q70HTTPListener;
import es.ejie.frmk.listeners.base.Q70LogDataUtils;
import es.ejie.frmk.presentacion.base.Q70Constantes;
import es.ejie.frmk.presentacion.sesiondatos.Q70ContextoEJIE;
import es.ejie.frmk.presentacion.sesiondatos.Q70SesionDatos;

/**
 * Clase que implementa la funcionalidad del Gestor De Canal HTTP Browser-PC y del componente <b>Listener HTTP</b>.
 * <p>
 * La doble funcionalidad es debida a que la clase <code>Q70GestorDeCanalHTTPBrowserPC</code> extiende de la
 * clase de Struts <code>ActionServlet</code>, en la que Struts basa su solución MVC. 
 * Se trata de una clase controladora de peticiones que encapsula las principales funcionalidades esperadas del 
 * Listener HTTP y del Gestor de Canal, como son:
 * <UL><b>Listener:</b>
 * <li>Escucha de peticiones http</li>
 * <li>Interpretación de la petición http recibida</li>
 * </UL>
 * <UL><b>Gestor de Canal:</b>
 * <li>Llamada a la Operación de Presentación adecuada</li>
 * </UL>
 *
 * @version 1.0
 * Fecha: 21-nov-03
 */
public class Q70GestorDeCanalHTTPBrowserPC extends ActionServlet implements Q70HTTPListener
{

	/**
	 * Constante que indica el componente al que pertenece dicha clase. Es
	 * necesario para el uso del log de trazas.
	 */
	private static final String COMPONENTE = "GESTOR_CANAL";

	/**
	 * Constante que indica el canal que gestionado.
	 */
	private static final String CANAL = "BROWSER-PC";

	/**
	 * Constante que indica la KEY donde se va a depositar el contador de niveles en la sesion.
	 */
	private static final String CONTADOR_DE_NIVELES = "contadorDeNiveles";

	/**
	 * Instancia del log de navegación
	 */
	private static Q70LogNavegacion logNav = null;

	/**
	 * Instancia del log de trazas
	 */
	private static Q70Traza tr = null;

	/**
	 * Método que procesa la petición recibida desde Listener HTTP.
	 * <p>
	 * Prepara los datos de la petición necesarios para ser registrados en el Log de Navegación,
	 * delega el procesamiento de la petición en el método <code>process</code> de la clase <B>ActionServlet</b> 
	 * de Struts, y captura los throwables no recogidos en capas posteriores.
	 *  
	 * @param <code>request</code> Petición a procesar
	 * @param <code>response</code> Respuesta a crear
	 * @throws <code>ServletException</code>
	 * @throws <code>IOException</code>
	 */
	protected void process(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException
	{
		//Si cuando se realiza una peticion por parte de un usuario, Geremua esta en medio de un test Junit,
		// entonces lanza una excepcion informando de lo sucedido y sale del metodo, sin realizar accion alguna mas.
		if ( Q70GestorArranqueConfiguracion.isTestCase){
			catchGenericThrowables(new Q70CanalException(this.getClass().toString(), "service", "Su peticion no ha sido procesada, porque el servidor está ejecuntando Test Junit (esta tarea puede llevar varios minutos). Intentelo mas tarde.", null), req, resp);
			return;
		}

		long time1 = 0;
		long time2 = 0;
		try
		{
			time1 = System.currentTimeMillis();
			if (null == logNav)
				logNav = Q70LogNavegacion.getSingleton();
			if (null == tr)
				tr = Q70Traza.getSingleton();

			Object contadorDeNiveles = req.getSession().getAttribute(CONTADOR_DE_NIVELES);
			if (contadorDeNiveles == null)
			{
				req.getSession().setAttribute(CONTADOR_DE_NIVELES, new Integer(0));
			}

			Q70LogDataUtils.initialPrepareLogData(req, CANAL, COMPONENTE);

			Q70ConectorJDBC.getSingleton().registrarCurrentThread();

			writeNavLogBegin();
			if (tr.isTraceActive())
			{
				tr.trace("HTTP_LISTENER", Q70TraceLevel.INFO, "Procesando petición.");
			}
			if (tr.isTraceActive())
			{
				tr.trace(COMPONENTE, Q70TraceLevel.DEBUG, "Procesando petición para la URI: " + req.getRequestURI());
			}

			Integer aux = (Integer) (req.getSession().getAttribute(CONTADOR_DE_NIVELES));
			req.getSession().setAttribute(CONTADOR_DE_NIVELES, new Integer(aux.intValue() + 1));

			if (aux.intValue() == 0)
			{
				doUserContextRefresh(req);
			}

			super.process(req, resp);

			aux = (Integer) (req.getSession().getAttribute(CONTADOR_DE_NIVELES));
			if (null == aux)
			{
				aux = new Integer(1);
			}

			req.getSession().setAttribute(CONTADOR_DE_NIVELES, new Integer(aux.intValue() - 1));
			aux = (Integer) (req.getSession().getAttribute(CONTADOR_DE_NIVELES));
			if (aux.intValue() == 0)
			{
				doUserContextPersistence(req);
			}
			Q70LogDataUtils.initialPrepareLogData(req, CANAL, COMPONENTE);
			writeNavLogEnd();
		}
		catch (ServletException e)
		{
			req.getSession().setAttribute(CONTADOR_DE_NIVELES, new Integer(0));
			if (tr.isTraceActive())
			{
				tr.trace(COMPONENTE, Q70TraceLevel.ERROR, "ServletException.");
			}
			catchGenericThrowables(new Q70CanalException(this.getClass().toString(), "service", "ServletException recogida", e), req, resp);
		}
		catch (IOException e)
		{
			req.getSession().setAttribute(CONTADOR_DE_NIVELES, new Integer(0));
			if (tr.isTraceActive())
			{
				tr.trace(COMPONENTE, Q70TraceLevel.ERROR, "IOException.");
			}
			catchGenericThrowables(new Q70CanalException(this.getClass().toString(), "service", "IOException recogida", e), req, resp);
		}
		catch (Throwable e)
		{
			req.getSession().setAttribute(CONTADOR_DE_NIVELES, new Integer(0));
			catchGenericThrowables(new Q70CanalException(this.getClass().toString(), "service", "Throwable recogida", e), req, resp);
		}
		finally
		{
			try
			{
				Integer aux = (Integer) (req.getSession().getAttribute(CONTADOR_DE_NIVELES));
				if (aux.intValue() == 0)
				{
					Q70ConectorJDBC.getSingleton().removeCurrentThreadResources();
					Q70LogDataUtils.clearLogData(COMPONENTE);
					time2 = System.currentTimeMillis();
					Q70MonitorTiemposAcceso.getSingleton().setAccessTime(time2 - time1);
				}
			}
			catch (Throwable t)
			{
				req.getSession().setAttribute(CONTADOR_DE_NIVELES, new Integer(0));
				catchGenericThrowables(new Q70CanalException(this.getClass().toString(), "service", "Throwable recogida", t), req, resp);
			}
		}
	}

	/**
	 * Método que trata todo <b>throwable</b> no capturado por capas posteriores.
	 * <p>
	 * Colabora con el <b>Gestor de Excepciones</b>, enviándole el throwable recogido, para que éste lo trate 
	 * y lo registre en el Log de Incidencias
	 * 
	 * @param <code>t</code> Throwable a tratar.
	 * @param <code>request</code> Petición a procesar
	 * @param <code>response</code> Respuesta a crear
	 */
	public void catchGenericThrowables(Throwable t, HttpServletRequest req, HttpServletResponse resp)
	{
		Q70GestorDeExcepciones.getSingleton().recogerThrowable(t, req, resp);
	}

	/**
	 * Método que registra un inicio de navegación en el log de navegación.
	 *  
	 * 
	 */
	public void writeNavLogBegin()
	{
		if (logNav.isEnabled())
		{
			logNav.log(Q70LogNavegacion.RESPUESTA_OK, Q70LogNavegacion.TIPO_NAVEGACION_ENTRADA);
		}
	}

	/**
	 * Método que registra un fin de navegación en el log de navegación.
	 * 
	 * 
	 */
	public void writeNavLogEnd()
	{
		if (logNav.isEnabled())
		{
			logNav.log(Q70LogNavegacion.RESPUESTA_OK, Q70LogNavegacion.TIPO_NAVEGACION_SALIDA);
		}
	}
/**
 * Método que recupera la estructura de contextos cuando ésta ha desaparecido de la sesión de usuario.
 * <p>
 * La sesión de usuario pierde la referencia a la estructura de contextos de usuario típicamente cuando caduca
 * en el servidor de aplicaciones por timeout o tiempo de inactividad del usuario. 
 * @param <code>req</code> Petición a procesar
 * @throws Exception
 */

	private void doUserContextPersistence(HttpServletRequest req) throws Exception
	{
		//Recuperación de contexto
		Q70ContextoEJIE contexto = (Q70ContextoEJIE) req.getSession().getAttribute(Q70Constantes.CONTEXT);
		if (contexto != null)
		{
			Q70SesionDatos.getSingleton().doSaveContextosDeSesion(contexto);
		}
	}
	
	private void doUserContextRefresh(HttpServletRequest req) throws Exception
	{
		//Recuperación de contexto
		Q70ContextoEJIE contexto = (Q70ContextoEJIE) req.getSession().getAttribute(Q70Constantes.CONTEXT);
		if (contexto != null)
		{
			Q70SesionDatos.getSingleton().doLoadContextosDeSesion(contexto);
		}
	}
}
