package es.ejie.frmk.control.eventosremotos;

import java.io.InputStream;
import java.util.HashMap;

import es.ejie.frmk.Q70Factoria;
import es.ejie.frmk.conectores.conectorJMS.Q70ConectorJMSExcepcion;
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.control.excepciones.Q70GestorDeExcepcionesServidor;
import es.ejie.frmk.control.serviciocoordinacion.Q70ServicioCoordinacion;
import es.ejie.frmk.infraestructura.traza.Q70TraceLevel;
import es.ejie.frmk.infraestructura.traza.Q70TrazaInterface;
import es.ejie.frmk.infraestructura.utilidades.configurador.Q70Configurable;
import es.ejie.frmk.infraestructura.utilidades.configurador.Q70Configurador;
import es.ejie.frmk.presentacion.sesiondatos.Q70ContextoEJIE;
import es.ejie.frmk.presentacion.sesiondatos.Q70ContextoEJIEObservador;

/**
 * Componente para distribuir los eventos remotos a los componentes interesados.
 * <p>
 * El <b>Gestor de Eventos Remotos</b> es un componente que permite a los demás componentes 
 * de infraestructura de Geremua, y a los componentes de aplicación, 
 * registrar su interés en un determinado evento remoto. 
 * Cuando dicho evento se produce en otro servidor de aplicaciones, 
 * el Gestor de Eventos Remotos recibe la notificación de que se ha producido el evento 
 * junto a los datos asociados al mismo. 
 * Su responsabilidad es entregar el evento a la clase escuchadora que sabrá cómo tratarlo.
 * <p>
 * Así mismo, el Gestor de Eventos Remotos escucha eventos internos que deben ser comunicados 
 * al resto de servidores de aplicaciones del sistema. 
 * La comunicación del evento, junto a sus datos asociados, 
 * es también tarea del Gestor de Eventos Remotos.
 * <p>
 * Llamamos evento interno a un evento del propio Geremua al que pertenece el Gestor de Eventos Remotos. 
 * No todos los eventos internos que se producen son interesantes de ser comunicados al resto de servidores, 
 * ya que únicamente afectan a componentes del propio Geremua al que pertenece el Gestor de Eventos Remotos, 
 * o a aplicaciones web en el propio servidor. 
 * Los eventos internos que deben ser comunicados serán los eventos internos comunicables:
 * <ul>
 * <li>Cambio de un dato global
 * <li>Cierre de la sesión de un usuario
 * </ul>
 *  
 * @see es.ejie.frmk.control.serviciocoordinacion.Q70ServicioCoordinacion
 * @see es.ejie.frmk.conectores.conectorJMS.Q70ConectorJMS
 */
public class Q70GestorEventosRemotos implements Q70Arrancable, Q70Configurable, Q70ContextoEJIEObservador {
	
	private static Q70GestorEventosRemotos singleton = null;
	private String ficheroConfiguracion = null;
	private String topic = null;
	private String nombreTopic = null;
	private HashMap listaTopics = null;
	private static final String ID_COMPONENTE = "GESTOR_EVENTOS_REMOTOS";
		
	/**
	 * Obtiene la instancia única de la clase, y en caso
	 * de que esta no exista, la crea.
	 *@return GestorEventosRemotos instancia única de la clase
	 */
	public static Q70GestorEventosRemotos getSingleton() {
		if (singleton == null)
			singleton = new Q70GestorEventosRemotos();
		return singleton;
	}
	
	public Q70GestorEventosRemotos() {
		super();
	}
	
	/** 
	 * Obtiene las factorias de conexiones y los topics del sistema
	 * y despierta el Gestor de Eventos Remotos para que se suscriba a los mismos
	 * @param file fichero de configuracion del componente
	 * @see es.ejie.frmk.control.arranque.Q70Arrancable#afterCreate(java.lang.String)
	 */
	public void afterCreate(String file) throws Q70FrameworkException {
		// Arrancar Gestor de Eventos Remotos y suscribirlo a los topic
		// correspondientes
		Q70TrazaInterface traza = Q70Factoria.getTraza();
		ficheroConfiguracion = file;
		if (traza.isTraceActive()){
			traza.trace(ID_COMPONENTE, Q70TraceLevel.DEBUG, "Gestor de Eventos Remotos --> afterCreate");
		}
		InputStream fis = Q70GestorArranqueConfiguracionServidor.getResourceAsStream(file);
		Q70Configurador configurador = new Q70Configurador(this, fis);
		try
		{
			configurador.generateConfiguration();
		}
		catch (Exception e)
		{
			if (traza.isTraceActive()){
				traza.trace(ID_COMPONENTE, Q70TraceLevel.ERROR, "Se ha producido un error en el método afterCreate de la clase Q70GestorEventosRemotos");
			}
			throw new Q70FrameworkException(ID_COMPONENTE,this.getClass().toString(),"afterCreate","Se ha producido un error en el método afterCreate.",e);
		}
	}

	/** 
	 * Método que realiza las tareas de pre-finalización del componente
	 * @see es.ejie.frmk.control.arranque.Q70Arrancable#beforeDestroy()
	 */
	public void beforeDestroy() {
		Q70TrazaInterface traza = Q70Factoria.getTraza();
		if (traza.isTraceActive()){
			traza.trace(ID_COMPONENTE, Q70TraceLevel.DEBUG, "Gestor de Eventos Remotos --> beforeDestroy");
		}
	}

	/**
	 * @see es.ejie.frmk.control.arranque.Q70Arrancable#reConfigure()
	 */
	public void reConfigure() throws Q70FrameworkException{
		try {
			beforeDestroy();
			afterCreate(ficheroConfiguracion);
		} catch (Exception e) {
			Q70TrazaInterface traza = Q70Factoria.getTraza();
			if (traza.isTraceActive()){
				traza.trace(ID_COMPONENTE, Q70TraceLevel.ERROR, "Se ha producido un error en el método reConfigure de la clase Q70GestorEventosRemotos");
			}
			throw new Q70FrameworkException(ID_COMPONENTE,this.getClass().toString(),"reConfigure","Se ha producido un error en el método reConfigure.",e);
		}
	}
	
	/**
	 * Método que se llama al iniciar la lectura del xml de configuración
	 * @see es.ejie.frmk.infraestructura.utilidades.configurador.Q70Configurable#beginConfiguration()
	 */
	public void beginConfiguration(){
	}

	/**
	 * Método que se llama al finalizar la lectura del xml de configuración
	 * @see es.ejie.frmk.infraestructura.utilidades.configurador.Q70Configurable#endConfiguration()
	 */
	public void endConfiguration() {
	}

	/**
	 * @see es.ejie.frmk.infraestructura.utilidades.configurador.Q70Configurable#beginList(java.lang.String, java.lang.String, java.lang.String)
	 */
	public void beginList(String strNombre, String strNivel , String strPath ){
		listaTopics = new HashMap();					
	}


	/**
	 * @see es.ejie.frmk.infraestructura.utilidades.configurador.Q70Configurable#endList(java.lang.String, java.lang.String, java.lang.String)
	 */
	public void endList(String strNombre, String strNivel, String strPath ){
	}
	
	/**
	 * @see es.ejie.frmk.infraestructura.utilidades.configurador.Q70Configurable#beginStructure(java.lang.String, java.lang.String, java.lang.String)
	 */
	public void beginStructure(String strNombre, String strNivel, String strPath){
		topic = strNombre;
	}
	
	/**
	 * @see es.ejie.frmk.infraestructura.utilidades.configurador.Q70Configurable#endStructure(java.lang.String, java.lang.String, java.lang.String)
	 */
	public void endStructure(String strNombre, String strNivel, String strPath){
		Q70TrazaInterface traza = Q70Factoria.getTraza();
		if (traza.isTraceActive()){
			traza.trace(ID_COMPONENTE, Q70TraceLevel.DEBUG, "Gestor de Eventos Remotos --> finEstructura");		
		}
		listaTopics.put(topic, nombreTopic);
		topic=null;
		nombreTopic=null;
		
		if (traza.isTraceActive()) {
			traza.trace(ID_COMPONENTE, Q70TraceLevel.DEBUG, "nombre del topic: " + nombreTopic);
		}									
	}
	
	/**
	 * @see es.ejie.frmk.infraestructura.utilidades.configurador.Q70Configurable#beginProperty(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
	 */
	public void beginProperty(String strNombre,String strValor,String strNivel, String strPath){
		Q70TrazaInterface traza = Q70Factoria.getTraza();
		if (traza.isTraceActive()){
			traza.trace(ID_COMPONENTE, Q70TraceLevel.DEBUG, "Gestor de Eventos Remotos --> inicioPropiedad");		
		}
		if (strNombre.equalsIgnoreCase("NombreTopic"))
			nombreTopic = strValor;
		
		if (traza.isTraceActive()){
			traza.trace(ID_COMPONENTE, Q70TraceLevel.DEBUG, "Propiedad-Valor: " + strNombre + "-" + strValor);		
		}
	}
	
	/**
	 * @see es.ejie.frmk.infraestructura.utilidades.configurador.Q70Configurable#endProperty(java.lang.String, java.lang.String, java.lang.String)
	 */
	public void endProperty(String strNombre, String strNivel, String strPath){
	}

	/**
	 * Publica el mensaje relativo a un cambio de valor en un dato del contexto.
	 * <p>
	 * Aporta toda la info necesaria para extraer un dato de un contexto y publicarlo 
	 * (contexto, tipo del contexto, identificador del contexto, 
	 * nombre de atributo y nuevo valor a depositar en el atributo 
	 * @see es.ejie.frmk.presentacion.sesiondatos.Q70ContextoEJIEObservador#update(es.ejie.frmk.presentacion.sesiondatos.Q70ContextoEJIE, java.lang.String, java.lang.String, java.lang.String, java.lang.Object)
	 */
	public void update(Q70ContextoEJIE contextoModificado, String tipoContexto, String idContexto,
						String nombreCampoModificado, Object nuevoValorCampoModificado){
		Q70TrazaInterface traza = Q70Factoria.getTraza();
		if (traza.isTraceActive()){
			traza.trace(ID_COMPONENTE, Q70TraceLevel.DEBUG, "Gestor de Eventos Remotos --> update: " + idContexto + " " + nombreCampoModificado + " " + nuevoValorCampoModificado);
		}
		HashMap datosContexto = new HashMap();
		datosContexto.put("tipo", tipoContexto);
		datosContexto.put("id", idContexto);
		datosContexto.put("campo", nombreCampoModificado);
		datosContexto.put("valor", nuevoValorCampoModificado);
		try{		
			Q70ServicioCoordinacion.getSingleton().publishMessage((String)listaTopics.get("contextoGlobal"), datosContexto);
		}catch(Q70ConectorJMSExcepcion cjmse){
			if (traza.isTraceActive()) {
				traza.trace(ID_COMPONENTE, Q70TraceLevel.ERROR, "GestorEventosRemotosExcepcion: " + nombreTopic);
			}									
			Q70GestorDeExcepcionesServidor.getSingleton().recogerThrowable(cjmse);	
		}

	}

	/**
	 * Comunica la desconexión de un usuario publicando un mensaje JMS.
	 * @param usuario identificador del usuario a desconectar
	 */
	public void desconectarUsuario(String usuario) {
		Q70TrazaInterface traza = Q70Factoria.getTraza();
		if (traza.isTraceActive()){
			traza.trace(ID_COMPONENTE, Q70TraceLevel.DEBUG, "Gestor de Eventos Remotos --> desconectar usuario: " + usuario);
		}
		try{		
			Q70ServicioCoordinacion.getSingleton().publishMessage((String)listaTopics.get("desconexionUsuario"), usuario);
		}catch(Q70ConectorJMSExcepcion cjmse){
			if (traza.isTraceActive()) {
				traza.trace(ID_COMPONENTE, Q70TraceLevel.ERROR, "GestorEventosRemotosExcepcion: (desconexionUsuario)");
			}									
			Q70GestorDeExcepcionesServidor.getSingleton().recogerThrowable(cjmse);	
		}		
	}
}
