package com.ejie.ad74a.jms.util;

import java.io.Serializable;
import java.util.Hashtable;
import java.util.Map;
import java.util.Map.Entry;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.ObjectMessage;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.apache.commons.collections.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Implementacion de las utilidades necesarias para que como resultado de la
 * tramitacion el sistema interactue con la mensajeria de colas
 * 
 * @author jhidalgo
 * 
 */
public class UtilJms {
	private UtilJms() {
	}

	/** Instancia del log a utilizar */
	private static final Logger LOGGER = LoggerFactory.getLogger(UtilJms.class);

	public static String enviarJMS(Object datos, String factoriaJndi, String colaJndi,
			Map<String, String> propiedadesExtra, Map<String, String> entorno) {

		UtilJms.LOGGER.trace("UtilJms: enviarJMS - INI");
		Context jndiContext = null;
		QueueConnectionFactory queueConnectionFactory = null;
		QueueConnection queueConnection = null;
		QueueSession queueSession = null;
		Queue queue = null;
		QueueSender queueSender = null;

		try {
			if (entorno == null) {
				jndiContext = new InitialContext();
			} else {
				jndiContext = new InitialContext(new Hashtable<String, String>(entorno));
			}
			// Preparamos la llamada a la cola jms
			queueConnectionFactory = (QueueConnectionFactory) jndiContext.lookup(factoriaJndi);
			queue = (Queue) jndiContext.lookup(colaJndi);
			queueConnection = queueConnectionFactory.createQueueConnection();
			queueSession = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
			queueSender = queueSession.createSender(queue);

			Message oMessage = preparaMensaje(datos, propiedadesExtra, queueSession);

			// ENVIO DEL MENSAJE A LA COLA DESTINO
			queueSender.send(oMessage);
			if (UtilJms.LOGGER.isTraceEnabled()) {
				UtilJms.LOGGER.trace(new StringBuilder("UtilJms: enviar JMS enviarJMS FIN, messageID: ")
						.append(oMessage.getJMSMessageID()).toString());
			}
			return oMessage.getJMSMessageID();
		} catch (NamingException e) {
			UtilJms.LOGGER.error("UtilJms: enviarJMS Error en metodo enviarJMS ", e);
			return null;
		} catch (JMSException e) {
			UtilJms.LOGGER.error("UtilJms: enviarJMS Error al crear/enviar el mensaje JMS ", e);
			return null;
		} finally {
			ejecutaFinally(jndiContext, queueConnection);
		}
	}

	/**
	 * Metodo de refactorizacion, para cumplir con la complejidad ciclomatica
	 * 
	 * @param datos
	 * @param propiedadesExtra
	 * @param queueSession
	 * @return
	 * @throws JMSException
	 */
	private static Message preparaMensaje(Object datos, Map<String, String> propiedadesExtra, QueueSession queueSession)
			throws JMSException {
		Message oMessage;
		// creamos el mensaje que queremos encolar con la informacion
		// recibida por par�metro.
		if (datos instanceof String) {
			// EL PARAMETRO QUE SE QUIERE ENVIAR ES UN STRING
			// Se utiliza el tipo TextMessage para encolar
			oMessage = queueSession.createTextMessage();
			// Como solo hay un valor que introducir en el mensaje, lo
			// metemos en el texto de este.
			((TextMessage) oMessage).setText((String) datos);
			// PROPIEDADES EXTRA DEL MENSAJE
			if (MapUtils.isNotEmpty(propiedadesExtra)) {
				for (Entry<String, String> entry : propiedadesExtra.entrySet()) {
					oMessage.setStringProperty(entry.getKey(), entry.getValue());
				}
			}
		} else if (datos instanceof Serializable) {
			// EL PARAMETRO QUE SE QUIERE ENVIAR ES UN OBJETO JAVA
			// SERIALIZABLE
			oMessage = queueSession.createObjectMessage();
			((ObjectMessage) oMessage).setObject((Serializable) datos);
		} else {
			throw new JMSException(
					"El mensaje no puede ser nulo y debe ser un String o implementar la interfaz Serializable. Mensaje: "
							+ datos + ".");
		}
		return oMessage;
	}

	/**
	 * Metodo de refactorizacion, para cumplir con la complejidad ciclomatica
	 * 
	 * @param jndiContext
	 * @param queueConnection
	 */
	private static void ejecutaFinally(Context jndiContext, QueueConnection queueConnection) {
		if (queueConnection != null) {
			try {
				queueConnection.close();
			} catch (JMSException e) {
				UtilJms.LOGGER.error("UtilJms: ejecutaFinally Error al cerrar la cola JMS: ", e);
			}
		}
		// Cerrar el contexto de envio a colas. Muy importante!!
		// Si no, cogera estos datos de contexto para la ejecucion
		// posterior...
		if (jndiContext != null) {
			try {
				jndiContext.close();
			} catch (NamingException e) {
				UtilJms.LOGGER.error("UtilJms: ejecutaFinally Error al cerrar el Contexto de envio JMS", e);
			}
		}
	}
}
