/*
 * Fecha: 27-ene-04
 */
package es.ejie.frmk.negocio.operacioninterna;

import java.util.HashMap;

import es.ejie.frmk.Q70Factoria;
import es.ejie.frmk.control.arranque.Q70Arrancable;
import es.ejie.frmk.control.arranque.Q70GestorArranqueConfiguracionServidor;
import es.ejie.frmk.control.excepciones.Q70FrameworkException;
import es.ejie.frmk.infraestructura.traza.Q70TraceLevel;
import es.ejie.frmk.infraestructura.traza.Q70TrazaInterface;

/**
 * @version 1.0
 * Fecha: 27-ene-04
 */
public class Q70FactoriaOperacionesInternas implements Q70Arrancable
{

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

	//private static Q70FactoriaOperacionesInternas instancia = null;

	private String xmlConfiguracion = null;
	private HashMap mappingOperacionesInternas = null;
	/**
	 * Conjunto de instancias de Operaciones Internas que han sido creadas e inicializadas,
	 * cuya clave es el nombre de la clase java totalmente calificada.
	 */
	protected HashMap operacionesInternas = new HashMap();

	/**
	 * método estático que obtiene el objeto singleton;
	 * si no existe, lo crea.
	 * @return un objeto Q70FactoriaOperacionesInternas
	 */
	public static Q70FactoriaOperacionesInternas getSingleton()
	{
		/*	if (instancia == null) {
			instancia = new Q70FactoriaOperacionesInternas();
		}
		return instancia;
		*/
		Q70FactoriaOperacionesInternas instancia =
			(Q70FactoriaOperacionesInternas)Q70GestorArranqueConfiguracionServidor.getAttribute(COMPONENTE);
		if (instancia == null)
		{
			instancia = new Q70FactoriaOperacionesInternas();
			Q70GestorArranqueConfiguracionServidor.setAttribute(COMPONENTE, instancia);

		}
		return instancia;

	}

	/**
	 * @see es.ejie.frmk.control.Arrancable#afterCreate(java.lang.String)
	 */
	public void afterCreate(String file) throws Q70FrameworkException
	{

		try {
			xmlConfiguracion = file;
			Q70ConfiguradorOperacionesInternas conf = new Q70ConfiguradorOperacionesInternas(file);
			conf.lanzarConfigurador();
		} catch (Q70FrameworkException e) {
			Q70TrazaInterface traza = Q70Factoria.getTraza();
			if (traza.isTraceActive()){
				traza.trace(COMPONENTE, Q70TraceLevel.ERROR, "Se ha producido un error en el método afterCreate de la clase Q70FactoriaOperacionesInternas");
			}
			throw new Q70FrameworkException(COMPONENTE,this.getClass().toString(),"afterCreate","Se ha producido un error en el método afterCreate.",e);		}
	}

	/**
	 * @see es.ejie.frmk.control.Arrancable#beforeDestroy()
	 */
	public void beforeDestroy()
	{
	}

	/**
	 * @see es.ejie.frmk.control.Arrancable#reConfigure()
	 */
	public void reConfigure() throws Q70FrameworkException
	{
		try {
			mappingOperacionesInternas.clear();
			mappingOperacionesInternas = null;

			operacionesInternas.clear();
			operacionesInternas = null;

			afterCreate(xmlConfiguracion);
		} catch (Exception e) {
			Q70TrazaInterface traza = Q70Factoria.getTraza();
			if (traza.isTraceActive()){
				traza.trace(COMPONENTE, Q70TraceLevel.ERROR, "Se ha producido un error en el método reConfigure de la clase Q70FactoriaOperacionesInternas");
			}
			throw new Q70FrameworkException(COMPONENTE,this.getClass().toString(),"reConfigure","Se ha producido un error en el método reConfigure.",e);		
		}
	}

	/**
	 * @param operacionInternaId
	 * @return
	 * @throws Exception
	 */
	public Q70OperacionInternaBase getOperacionInterna(String operacionInternaId) throws Exception
	{

		Q70OperacionInternaBase instance = null;

		try
		{
			Q70TrazaInterface tr = Q70Factoria.getTraza();
			// Recoge la instancia de Operacion Interna a usar (si existe)
			if (tr.isTraceActive())
			{
				tr.trace(COMPONENTE, Q70TraceLevel.DEBUG, "Buscando instancia de OI = " + operacionInternaId + " en Pool");
			}
			Q70OperacionInternaConfigBean oi_config = new Q70OperacionInternaConfigBean();
			oi_config = (Q70OperacionInternaConfigBean)mappingOperacionesInternas.get(operacionInternaId);
			if (null == oi_config)
			{
				if (tr.isTraceActive())
				{
					tr.trace(
						COMPONENTE,
						Q70TraceLevel.ERROR,
						"Error al recuperar la configuración de la OI con ID="
							+ operacionInternaId
							+ ". Asegurese de haberla declarado en el configuracionOI.xml");
				}
			}

			String className = null;
			className = oi_config.getOiClase();
			String oiName = null;
			oiName = oi_config.getOiId();
			if ((null == oiName) || (0 == oiName.trim().length()))
			{
				if (tr.isTraceActive())
				{
					tr.trace(
						COMPONENTE,
						Q70TraceLevel.ERROR,
						"Error al recuperar la Clase implementadora de OI. Asegurese de haberla declarado en el configuracionOI.xml");
				}
			}
			else
			{
				if (tr.isTraceActive())
				{
					tr.trace(COMPONENTE, Q70TraceLevel.DEBUG, "Buscando clase implementadora de OI = " + oiName);
				}
				synchronized (operacionesInternas)
				{

					// Devuelve cualquier instancia de la oi buscada
					instance = (Q70OperacionInternaBase)operacionesInternas.get(oiName);
					if (null != instance)
					{
						if (tr.isTraceActive())
						{
							tr.trace(COMPONENTE, Q70TraceLevel.INFO, "Devolviendo una instancia de OI existente en Pool");
						}
						return (instance);
					}

					try
					{
						//Crea y devuelve una nueva instancia de Q70OperacionInternaBase.
						if (tr.isTraceActive())
						{
							tr.trace(COMPONENTE, Q70TraceLevel.INFO, "Creando nueva instancia de OI");
						}
						instance = (Q70OperacionInternaBase)applicationInstance(className);
						if (tr.isTraceActive())
						{
							tr.trace(COMPONENTE, Q70TraceLevel.INFO, "Configurando la nueva instancia de OI");
						}
						instance.setConfiguracion(oi_config);
					}
					catch (Exception e2)
					{
						if (tr.isTraceActive())
						{
							tr.trace(COMPONENTE, Q70TraceLevel.INFO, "Error en la creación de nueva instancia de OI");
						}
						throw e2;
					}
					operacionesInternas.put(oiName, instance);
					if (tr.isTraceActive())
					{
						tr.trace(COMPONENTE, Q70TraceLevel.INFO, "Nueva instancia de OI depositada en Pool");
					}
				}
			}
		}
		catch (Exception e)
		{
			throw new Q70OperacionInternaException(
				this.getClass().toString(),
				"getOperacionInterna",
				"Excepción al crear/recuperar una instancia de OI",
				e);
		}
		return (instance);
	}

	/**
	 * Return a new instance of the specified fully qualified class name,
	 * after loading the class from this web application's class loader.
	 * The specified class <strong>MUST</strong> have a public zero-arguments
	 * constructor.
	 *
	 * @param className Fully qualified class name to use
	 * @return new instance of class
	 * @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());

	}
	/**
	 * Return the <code>Class</code> object for the specified fully qualified
	 * class name, from this web application's class loader.
	 *
	 * @param className Fully qualified class name to be loaded
	 * @return Class object
	 * @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 = Q70FactoriaOperacionesInternas.class.getClassLoader();
		}
		// Attempt to load the specified class
		return (classLoader.loadClass(className));

	}
	/**
	 * @param map
	 */
	protected void setMappingOperacionesInternas(HashMap map)
	{
		mappingOperacionesInternas = map;
	}

}
