package es.ejie.frmk.presentacion.operacion.paso;

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

import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import es.ejie.frmk.infraestructura.traza.Q70TraceLevel;
import es.ejie.frmk.infraestructura.traza.Q70Traza;
import es.ejie.frmk.presentacion.base.Q70PasosOPMapping;
import es.ejie.frmk.presentacion.operacion.Q70DefaultRouterClass;
import es.ejie.frmk.presentacion.operacion.Q70OperacionDePresentacionException;
import es.ejie.frmk.presentacion.sesiondatos.Q70ContextoEJIE;

/**
 * Esta clase implementa un tipo de paso de OP que invoca a negocio (OI) de forma declarativa y que 
 * no necesita ser extendido por el desarrollador de la aplicación. 
 * <p>
 * Podrá usarse cuando:
 * <UL>
 * <li>el tratamiento no necesita manipulación de datos del contexto previa o posterior a la invocación
 * de la OI (idem que Q70ActionOIRouterDeclartivo)</li> y
 * <li>se configura la clase <code>Q70DefaultRouterClass</code> o una extensión de ella por parte de la 
 * aplicación para resolver la decisión de enrutamiento</li> 
 * </UL>
 * Basta con indicarle por parámetro de configuración de la OP  el identificador de la OI a ejecutar 
 * (propiedad <code>parameter</code>) y la clase que implementa su decisión de encaminamiento 
 * (propiedad <code>routerClass</code>).Esta clase extenderá de la clase Q70DefaultRouterClass. Si no se 
 * le indica ninguna routerClass, Geremua aporta una clase por defecto (<code>Q70DefaultRouterClass</code>)
 * que dirige el flujo hacia una salida (<code>forward</code>) de tipo "ok" o tipo "error". La clase 
 * <code>Q70DefaultRouterClass</code> devuelve indicación de enrutamiento "ok" cuando no ha habido errores y 
 * "error" en caso contrario. Si este patrón es válido, la aplicación no necesitará extender ninguna clase de
 * Geremua y el proceso sería 100% declarativo. Si se da un patrón de decisión que se base en otros factores 
 * (por ejemplo, por cálculos resultantes de ir a negocio), se deberá extender <code>Q70DefaultRouterClass</code>.
 *  
 */

public class Q70ActionOIRouterDeclarativo extends Q70ActionOIDeclarativo {
	
	private static final String DEFAULT_ROUTER_CLASS = "es.ejie.frmk.presentacion.operacion.Q70DefaultRouterClass";
	
	/**
	 * Constante que indica el componente al que pertenece la clase. Es
	 * necesario para el uso del log de trazas.
	 */
	private static final String COMPONENTE = "OPERACION_PRESENTACION";

	/**
	 * Instancia del log de trazas
	 */
	private static Q70Traza tr = Q70Traza.getSingleton();

	/**
	 * Este método obtiene la instancia de clase enrutadora,y ejecuta su método <code>processForward</code>.
	 * 
	 * @see es.ejie.frmk.presentacion.operacion.paso.Q70ActionPasoOP#processForward(es.ejie.frmk.presentacion.sesiondatos.Q70ContextoEJIE, org.apache.struts.action.ActionErrors, org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
	 */
	protected ActionForward processForward(Q70ContextoEJIE contexto, ActionErrors errores, ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception
	{
		if (tr.isTraceActive())
		{
			tr.trace(COMPONENTE, Q70TraceLevel.INFO, "Llamando al método processForward de la clase de Q70ActionOIRouterDeclarativo...");
		}
		String routerClass = ((Q70PasosOPMapping)mapping).getRouterClass();
		
		if (null == routerClass)
		{
			routerClass = DEFAULT_ROUTER_CLASS;
			if (tr.isTraceActive())
			{
				tr.trace(COMPONENTE, Q70TraceLevel.INFO, "DEFAULT_ROUTER_CLASS establecida como clase para decidir el ActionForward.");
			}
		}
		else
		{
			if (tr.isTraceActive())
			{
				tr.trace(COMPONENTE, Q70TraceLevel.DEBUG, routerClass+" establecida como clase para decidir el ActionForward.");
			}
		}
		
		Q70DefaultRouterClass instance = null;
		try
		{
			instance = (Q70DefaultRouterClass)applicationInstance(routerClass);
			if (tr.isTraceActive())
			{
				tr.trace(COMPONENTE, Q70TraceLevel.INFO, "Llamando al método processForward de la clase "+routerClass);
			}
			return instance.processForward(contexto, errores, mapping);
		}
		catch (Exception e)
		{
			if (tr.isTraceActive())
			{
				tr.trace(COMPONENTE, Q70TraceLevel.ERROR, "Excepción recogida por Action invocador de Operación Interna con routerClass y declarativo.");
			}
			throw new Q70OperacionDePresentacionException (this.getClass().toString(),"processForward","Excepción recogida por Action invocador de Operación Interna con routerClass y declarativo.",e);
		}
	}

	/**
	 * Este método devuelve una nueva instancia de una clase através del
	 * class loader de la web-application.
	 *
	 * @param <code>className</code> Nombre de la clase "fully qualified"
	 * @return Nueva instancia de la clase
	 * @exception ClassNotFoundException if the class cannot be found
	 * @exception IllegalAccessException if the class or its constructor
	 *  is not accessible
	 * @exception InstantiationException if this class represents an
	 *  abstract class, an interface, an array class, a primitive type,
	 *  or void
	 * @exception InstantiationException if this class has no
	 *  zero-arguments constructor
	 */
	private static Object applicationInstance(String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException
	{
		return (applicationClass(className).newInstance());
	}
	
	/**
	 * Este método devuelve el objeto "Class" através del class loader de la web-application.
	 *
	 * @param <code>className</code> Nombre de la clase "fully qualified"
	 * @return Objeto <code>Class</code>
	 * @exception ClassNotFoundException if the class cannot be found
	 */
	private static Class applicationClass(String className) throws ClassNotFoundException {

		// Look up the class loader to be used
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		if (classLoader == null) {
			classLoader = Q70ActionOIRouterDeclarativo.class.getClassLoader();
		}
		// Attempt to load the specified class
		return (classLoader.loadClass(className));

	}	
}
