package es.ejie.frmk.conectores.conectorJMS;

import java.util.HashMap;
import java.util.Iterator;

import javax.jms.JMSException;
import javax.jms.Session;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicSession;

import es.ejie.frmk.Q70Factoria;
import es.ejie.frmk.conectores.Q70ConectorBase;
import es.ejie.frmk.control.excepciones.Q70FrameworkException;
import es.ejie.frmk.control.excepciones.Q70GestorDeExcepcionesServidor;
import es.ejie.frmk.infraestructura.traza.Q70TraceLevel;
import es.ejie.frmk.infraestructura.traza.Q70TrazaInterface;


/**
 * El <b>Conector JMS</b> es el punto único de entrada a los componentes de negocio que desean escribir o leer un mensaje en una cola JMS.
 * <p>
 * Es un componente de soporte para el Servicio de Coordinación y las Aplicaciones Web 
 * que vayan a utilizar el servicio de envío de mensajes.
 * <p> 
 * El Conector JMS proporciona los objetos de infraestructura y la lógica de programación 
 * con los que se realiza la comunicación de mensajes entre componentes, 
 * aplicaciones web y/o servidores de aplicación.
 * 
 * @see es.ejie.frmk.control.eventosremotos.Q70GestorEventosRemotos
 * @see es.ejie.frmk.control.serviciocoordinacion.Q70ServicioCoordinacion
 * @version 1.0
 * Fecha: 05-feb-04
 */
public class Q70ConectorJMS extends Q70ConectorBase implements Q70ConectorJMSInterface{
	private static Q70ConectorJMS conectorJMS = null;	
	private HashMap conexiones = new HashMap();
	private HashMap ubicaciones = new HashMap();

	private Q70ConectorJMS(){
	}

	/**
	 * Devuelve la referencia al Componente. Esta referencia es unica y no puede ser instanciada por el Constructor, ya que este es privado.
	 * @return Referencia al Componente
	 */
	public static Q70ConectorJMS getSingleton() {
		if (conectorJMS == null) {
			conectorJMS = new Q70ConectorJMS();
		}
		return conectorJMS;
	}
	
	public void beforeDestroy()
	{
		String ubicacion = null;
		Iterator it = (Iterator)ubicaciones.keySet().iterator();
		while(it.hasNext()){
			ubicacion = (String)it.next();
			closeConnection(ubicacion);
			//removeListener(ubicacion); Los subscriptores al finalizar el servicio realizan esta operacion
		}
	}

	public void reConfigure() throws Q70FrameworkException
	{
		try {
			beforeDestroy();
			configure();
		} catch (Q70FrameworkException e) {
			Q70TrazaInterface traza = Q70Factoria.getTraza();
			if (traza.isTraceActive()){
				traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.ERROR, "Se ha producido un error en el método reConfigure de la clase Q70ConectorJMS");
			}
			throw new Q70FrameworkException(Q70Constantes.ID_COMPONENTE,this.getClass().toString(),"reConfigure","Se ha producido un error en el método reConfigure.",e);
		}
	}

	protected void configure() throws Q70FrameworkException
	{		
		if (fileNameConfiguracion != null) {
			try {
				Q70Configurador conf = new Q70Configurador(fileNameConfiguracion);
				conf.lanzarConfigurador();
				this.contextos = conf.getContextos();
				this.ubicaciones = conf.getDestinations();
			} catch (Exception e) {
				Q70TrazaInterface traza = Q70Factoria.getTraza();
				if (traza.isTraceActive()){
					traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.ERROR, "Se ha producido un error en el método configure de la clase Q70ConectorJMS");
				}
				throw new Q70FrameworkException(Q70Constantes.ID_COMPONENTE,this.getClass().toString(),"configure","Se ha producido un error en el método configure.",e);
			}
		}		
	}
	
	/**
	 * @see es.ejie.frmk.conectores.conectorJMS.Q70ConectorJMSInterface#getConnection(java.lang.String, java.lang.String, java.lang.String)
	 */
	public TopicConnection getConnection(String usuario, String password, String ubicacion) throws Q70ConectorJMSExcepcion{
		TopicConnectionFactory factory = null;
		TopicConnection connection = null;
		Q70TrazaInterface traza = Q70Factoria.getTraza();
		if (traza.isTraceActive()){
			traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "ConectorJMS --> getConnection");
		}
		//Si existe ya esa conexion se devuelve y si no, se crea
		connection = (TopicConnection)conexiones.get(ubicacion);
		if (connection != null)
			return connection;	
		try{
			if (contextos.get(ubicacion) == null)
				contexto = getContexto();
			else			
				contexto = getContexto(ubicacion);
			String connectionFactoryName = null;
			try{
				connectionFactoryName = (String)ubicaciones.get(ubicacion);
			}catch(Throwable e){}
			if(connectionFactoryName == null)
				connectionFactoryName = ubicacion;
			factory = (TopicConnectionFactory)contexto.lookup(connectionFactoryName);
			if(usuario != null && password != null) 
				connection = factory.createTopicConnection(usuario, password);
			else                                     
				connection = factory.createTopicConnection();
			connection.start();
			conexiones.put(ubicacion, connection);
		}
		catch(Exception e){
			throw new Q70ConectorJMSExcepcion(this.getClass().getName(), " getConnection", "Error conexion JMS", e);		
		}
		return connection;
	}
	
	/**
	 * @see es.ejie.frmk.conectores.conectorJMS.Q70ConectorJMSInterface#getConnection(java.lang.String)
	 */
	public TopicConnection getConnection(String ubicacion) throws Q70ConectorJMSExcepcion{
		return getConnection(null, null, ubicacion);
	}
	
	/**
	 * @see es.ejie.frmk.conectores.conectorJMS.Q70ConectorJMSInterface#getSession(java.lang.String)
	 */
	public TopicSession getSession(String ubicacion) throws Q70ConectorJMSExcepcion{
		TopicSession session = null;		
		Q70TrazaInterface traza = Q70Factoria.getTraza();
		if (traza.isTraceActive()){
			traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "ConectorJMS --> getSession");
		}	
		try{
			session = ((TopicConnection)conexiones.get(ubicacion)).createTopicSession(false, Session.AUTO_ACKNOWLEDGE);	
		}
		catch(Exception e){
			throw new Q70ConectorJMSExcepcion(this.getClass().getName(), " getSession", "Error sesion JMS", e);		
		}
		return session;
	}
	
	/**
	 * @see es.ejie.frmk.conectores.conectorJMS.Q70ConectorJMSInterface#getTopic(java.lang.String, java.lang.String)
	 */
	public Topic getTopic(String nombreTopic, String ubicacion) throws Q70ConectorJMSExcepcion{
		Topic unTopic = null;
		Q70TrazaInterface traza = Q70Factoria.getTraza();
		if (traza.isTraceActive()){
			traza.trace(Q70Constantes.ID_COMPONENTE, Q70TraceLevel.DEBUG, "ConectorJMS --> getTopic");
		}	
		try{
			contexto = getContexto(ubicacion);
			unTopic = (Topic)contexto.lookup(nombreTopic);
		}
		catch(Exception e){
			throw new Q70ConectorJMSExcepcion(this.getClass().getName(), " getTopic", "Error Topic JMS", e);		
		}
		return unTopic;
	}

	/**
	 * Cierra una conexión (y todas sus sesiones) con una ubicación dada. 
	 * @param ubicacion
	 */
	public void closeConnection(String ubicacion) {
		try{
			TopicConnection tc = (TopicConnection)conexiones.get(ubicacion);
			if (null != tc) {
				tc.close();
				conexiones.remove(ubicacion);
			}
		}catch(JMSException e){
			Q70GestorDeExcepcionesServidor.getSingleton().recogerThrowable(e);
		}
	}
}
