package es.ejie.frmk.control.monitor;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.DynaProperty;
import org.apache.commons.collections.CollectionUtils;

import es.ejie.frmk.Q70Factoria;
import es.ejie.frmk.admon.contextos.Q70InformacionContexto;
import es.ejie.frmk.admon.logs.Q70ConsultorLogs;
import es.ejie.frmk.conectores.conectorJDBC.Q70ConectorJDBC;
import es.ejie.frmk.control.arranque.Q70GestorArranqueConfiguracionServidor;
import es.ejie.frmk.control.arranque.Q70Instancia;
import es.ejie.frmk.control.eventosremotos.Q70GestorEventosRemotos;
import es.ejie.frmk.control.excepciones.Q70FrameworkException;
import es.ejie.frmk.infraestructura.seguridad.Q70Constantes;
import es.ejie.frmk.infraestructura.seguridad.Q70GestorSeguridad;
import es.ejie.frmk.infraestructura.seguridad.Q70InformacionSeguridadInterface;
import es.ejie.frmk.infraestructura.seguridad.Q70SeguridadException;
import es.ejie.frmk.presentacion.sesiondatos.Q70ContextoEJIE;
import es.ejie.frmk.presentacion.sesiondatos.Q70ContextoGlobal;
import es.ejie.frmk.presentacion.sesiondatos.Q70ContextoJerarquicoEJIE;
import es.ejie.frmk.presentacion.sesiondatos.Q70ContextoSesionUsuario;
import es.ejie.frmk.presentacion.sesiondatos.Q70SesionDatos;

/**
 * Este bean servirá para realizar ciertas acciones sobre el funcionamiento de Geremua y de las aplicaciones;
 * y  monitorizar su correcto funcionamiento.
 * <p>
 * Las acciones que se pueden realizar sobre Geremua son:
 * <ul>
 * <li>Invalidar la sesion de un usuario de Geremua de todos los Geremuas activos
 * <li>Modificar y refrescar datos de los contextos globales de Geremua
 * <li>Reconfigurar en caliente los Componentes de Geremua (JDBC, Apilamiento, JMS, Gestor Eventos, Servicio Coordinacion, Gestor Seguridad, Sesion Datos)
 * <li>Reescribir los ficheros de propiedades de Geremua, aunque no realiza la reconfiguración en caliente de los Componentes con las modificaciones de las propiedades
 * <li>Eliminar los recursos de Geremua de un thread.
 * </ul>
 * <p>
 * <p>
 * Sus funcionalidades en cuanto a la monitorización de Geremua son:
 * <ul>
 * <li>Proporcionar información del estado de la memoria
 * <li>Proporcionar información de tiempos de respuesta (extraída del log)
 * <li>Proporcionar información de threads activos
 * <li>Proporcionar información de los usuarios activos
 * <li>Proporcionar información de los datos almacenados por los contextos globales
 * <li>Proporcionar información sobre consultas de los logs y traza de Geremua
 * <li>Proporcionar información sobre el contenido del fichero de propiedades de Geremua y de las aplicaciones
 * <li>Proporcionar información sobre la configuracion de Componentes de Geremua 
 * (JDBC, Apilamiento, Gestor Arranque, JMS, Gestor Eventos, Servicio Coordinacion, Gestor Seguridad, Sesion Datos)
 * <li>Proporcionar información sobre las instancias activas de Geremua en un momento actual en el sistema
 * </ul>
 * <p>
 * Respecto a la monitorización de aplicaciones, 
 * ver los métodos <b>validarEstadoAplicacion</b>, <b>consultarEstadoAplicacion</b> y <b>refrescarEstadoAplicacion</b>.
 * 
 * @version 1.0
 * Fecha: 08-mar-04
 */
public class Q70BeanMonitorizacion implements Q70BeanMonitorizacionMBean
{
	private static final long serialVersionUID = 1L;

	private static final String NOMBRE_FICHERO_RESULTADOS_BEAN_MONITORIZACION = "beanMonitorizacion.txt";
	
	/**
	 * @see es.ejie.frmk.control.monitor.Q70BeanMonitorizacionMBean#getMemorySpace()
	 */
	public long getMemorySpace()
	{
		return Runtime.getRuntime().freeMemory();
	}
	/**
	 * @see es.ejie.frmk.control.monitor.Q70BeanMonitorizacionMBean#getTotalMemory()
	 */
	public long getTotalMemory()
	{
		return Runtime.getRuntime().totalMemory();
	}

	/**
	 * @see es.ejie.frmk.control.monitor.Q70BeanMonitorizacionMBean#getAverageAccessTime()
	 */
	public long getAverageAccessTime()
	{
		return Q70MonitorTiemposAcceso.getSingleton().getAverageTime();
	}

	/**
	 * @see es.ejie.frmk.control.monitor.Q70BeanMonitorizacionMBean#getThreadResources()
	 */
	public List getThreadResources()
	{
		return Q70ConectorJDBC.getSingleton().getAllThreadResources();
	}
	
	/**
	 * @see es.ejie.frmk.control.monitor.Q70BeanMonitorizacionMBean#removeThreadResources(java.lang.String)
	 */
	public void removeThreadResources (String nameThread)
	{
		Q70ConectorJDBC.getSingleton().removeThreadResources(nameThread);
	}
	
	/**
	 * @see es.ejie.frmk.control.monitor.Q70BeanMonitorizacionMBean#doInvalidateUsuarioJMS(java.lang.String)
	 */
	public void doInvalidateUsuarioJMS(String idContexto) throws Q70SeguridadException
	{
		Q70SesionDatos.getSingleton().invalidateAllContextoSesionUsuario(idContexto);
		Q70GestorEventosRemotos.getSingleton().desconectarUsuario(idContexto);
	}

	/**
	 * @see es.ejie.frmk.control.monitor.Q70BeanMonitorizacionMBean#refrescarEstadoAplicacion()
	 */
	public List refrescarEstadoAplicacion() {
		ArrayList mensajeBean = new ArrayList(1);
		mensajeBean.add("Para este Bean no hay una implementacion");
		return mensajeBean;
	}		
	
	/**
	 * @see es.ejie.frmk.control.monitor.Q70BeanMonitorizacionMBean#consultarEstadoAplicacion()
	 */
	public String consultarEstadoAplicacion()
	{
		return "Framework Q70";
	}

	/**
	 * @see es.ejie.frmk.control.monitor.Q70BeanMonitorizacionMBean#validarEstadoAplicacion()
	 */
	public Object[] validarEstadoAplicacion()
	{
		try
		{
			Vector mensajesError = new Vector();
			String[] validacionesBD = validarConexionesABD();
			String[] resultados = generarResultados();
			Object[] validacionesContraFichero = validarResultadosContraFichero(resultados);

			if (validacionesBD != null && validacionesBD.length > 0)
				for (int i = 0; i < validacionesBD.length; i++)
					mensajesError.addElement(validacionesBD[i]);

			if (validacionesContraFichero != null && validacionesContraFichero.length > 0)
				for (int i = 0; i < validacionesContraFichero.length; i++)
					mensajesError.addElement(validacionesContraFichero[i]);

			Object[] ret = new Object[mensajesError.size()];
			for (int i = 0; i < mensajesError.size(); i++)
				ret[i] = mensajesError.elementAt(i);
			return ret;
		}
		catch (Throwable t)
		{
			//escribirLog("Error validando aplicación");
			return null;
		}
	}
	
	
	/**
	 *@param resultados
	 *@return Object[] 
	 */
	private Object[] validarResultadosContraFichero(String[] resultados)
	{
		BufferedReader br=null;
		br =
			new BufferedReader(new InputStreamReader(Q70GestorArranqueConfiguracionServidor.getApplicationDataAsStream(this.getClass(),getNombreFicheroResultados())));
		if (null==br){
			br =
				new BufferedReader(new InputStreamReader(Q70GestorArranqueConfiguracionServidor.getApplicationDataAsStream(getNombreFicheroResultados())));			
		}
		Vector retVector = new Vector();
		Object[] ret = null;
		String resultadoCorrecto = null;
		String resultadoCalculado = null;
		int i = 0;
		try
		{
			while ((resultadoCorrecto = br.readLine()) != null)
			{
				i++;
				resultadoCorrecto = resultadoCorrecto.trim();
				if (resultados.length >= i)
				{
					resultadoCalculado = resultados[i - 1];
					resultadoCalculado = resultadoCalculado.trim();
					if (resultadoCorrecto.equalsIgnoreCase(resultadoCalculado))
						retVector.addElement("Q70Prueba " + i + ": Resultado correcto (" + resultadoCorrecto + ")");
					else
						retVector.addElement(
							"Q70Prueba "
								+ i
								+ ": Resultado incorrecto (Valor correcto: "
								+ resultadoCorrecto
								+ " Valor calculado: "
								+ resultadoCalculado
								+ ")");
				}
				else
					retVector.addElement(
						"El fichero de resultados incluye un resultado adicional " + resultadoCorrecto + " que no se ha podido comparar");
			}
		}
		catch (Exception e)
		{
			//Escribir un log con el error
		}
		try
		{
			br.close();
		}
		catch (Exception e)
		{}
		ret = new Object[retVector.size()];
		for (int j = 0; j < retVector.size(); j++)
			ret[j] = retVector.elementAt(j);
		return ret;
	}
	
	/**
	 * Devuelve los resultados de una serie de pruebas.
	 * <p>
	 * Las aplicaciones que suministren un bean de monitorización
	 * deberán reescribir este método haciendo pruebas de que
	 * la aplicación dispone del entorno necesario para ejecutarse
	 * correctamente.
	 * <p>
	 * Se guarda el resultado de dichas comprobaciones en 
	 * un array de strings.
	 * <p>
	 * Un ejemplo sería comprobar que determinada tabla tiene 50 filas.
	 * Se devolvería el número de filas que hay, por ejemplo 49, y este
	 * valor posteriormente se compara con el esperado.
	 */
	protected String[] generarResultados()
	{
		return new String[] { "Framework Q70" };
	}
	
	/**
	 * Las aplicaciones que suministren un bean de monitorización
	 * deberán reescribir este método haciendo las comprobaciones necesarias
	 */
	protected String[] validarConexionesABD()
	{
		return null;
	}
	
	/**
	 * @see es.ejie.frmk.control.monitor.Q70BeanMonitorizacionMBean#getNombreFicheroResultados()
	 */
	public String getNombreFicheroResultados()
	{
		return NOMBRE_FICHERO_RESULTADOS_BEAN_MONITORIZACION;
	}
	
	/**
	 * @see es.ejie.frmk.control.monitor.Q70BeanMonitorizacionMBean#recuperaConfiguracionComponente(java.lang.String)
	 */
	public String recuperaConfiguracionComponente(String nombreComponente) throws Q70FrameworkException { 
		return Q70GestorArranqueConfiguracionServidor.getConfiguracionComponente(nombreComponente);
	}
	
	/**
	 * @see es.ejie.frmk.control.monitor.Q70BeanMonitorizacionMBean#recuperaFicheroPropiedades(java.lang.String)
	 */
	public String recuperaFicheroPropiedades (String codigoAplicacion) throws Q70FrameworkException {
		return Q70GestorArranqueConfiguracionServidor.getContenidoFicheroPropiedades(codigoAplicacion);
	}
	
	/**
	 * @see es.ejie.frmk.control.monitor.Q70BeanMonitorizacionMBean#reconfigurarComponente(java.lang.String, java.lang.String)
	 */
	public boolean reconfigurarComponente(String nombreComponente, String xmlConfiguracion) throws Q70FrameworkException {
		return Q70GestorArranqueConfiguracionServidor.reconfigurarComponente(nombreComponente, xmlConfiguracion);
	}
	
	/**
	 * @see es.ejie.frmk.control.monitor.Q70BeanMonitorizacionMBean#reconfigurarFicheroPropiedades(codAplicacion, contenidoFichero)
	 */
	public boolean reconfigurarFicheroPropiedades(String codAplicacion, String contenidoFichero) throws Q70FrameworkException {
		return Q70GestorArranqueConfiguracionServidor.reconfigurarFicheroPropiedades (codAplicacion, contenidoFichero);
	}
	
	/**
	 * @see es.ejie.frmk.control.monitor.Q70BeanMonitorizacionMBean#getListaLogsTraza(consultorLogs)
	 */
	public List getListaLogsTraza(Q70ConsultorLogs consultorLogs) throws Q70FrameworkException 
	{
		return consultorLogs.executeQuery();
	}
	
	/**
	 * @see es.ejie.frmk.control.monitor.Q70BeanMonitorizacionMBean#getAplicacionesInstancia()
	 */
	public Collection getAplicacionesInstancia (Q70Instancia instancia) throws Q70FrameworkException {
		Collection colAplicaciones = Q70Factoria.getListaDeAplicaciones();
		colAplicaciones.add(Q70GestorArranqueConfiguracionServidor.CODIGO_APLICACION_FRAMEWORK);
		return colAplicaciones; 
	}

	/**
	 * @see es.ejie.frmk.control.monitor.Q70BeanMonitorizacionMBean#getContextosGlobales()
	 */
	public Collection getContextosGlobales() throws Q70SeguridadException
	{
		Collection contextosGlobales = Q70SesionDatos.getSingleton().getContextosGlobales();
		ArrayList listaContextosGlobales = new ArrayList();
		for (Iterator iter = contextosGlobales.iterator(); iter.hasNext();) {
			Q70ContextoGlobal element = (Q70ContextoGlobal) iter.next();
			Q70InformacionContexto contextoGlobal = new Q70InformacionContexto();
			contextoGlobal.setIdContexto(element.getIdContexto());
			contextoGlobal.setTipoContexto(element.getTipoContexto());
			listaContextosGlobales.add(contextoGlobal);
		}
		return listaContextosGlobales;
	}
	
	/**
	 * @see es.ejie.frmk.control.monitor.Q70BeanMonitorizacionMBean#getContextosUsuarios()
	 */
	public Collection getContextosUsuarios() {
		Collection contextosUsuarios = Q70SesionDatos.getSingleton().getContextosUsuarios();
		if (contextosUsuarios == null){
			return null;
		}
		
		Iterator it = contextosUsuarios.iterator();
		ArrayList listaUsuarios = new ArrayList();
		
		while (it.hasNext()){
			Q70ContextoSesionUsuario contextoSesionUsuario = (Q70ContextoSesionUsuario)it.next();
			
			Q70InformacionContexto informacionUsuario = new Q70InformacionContexto();
			informacionUsuario.setTipoContexto(contextoSesionUsuario.getTipoContexto());
			try {				
				Q70GestorSeguridad gs = Q70GestorSeguridad.getSingleton();
				
				informacionUsuario.setIdContexto(gs.getSessionId((Q70InformacionSeguridadInterface)contextoSesionUsuario.get(Q70Constantes.SEGURIDAD_USUARIO)));
				Vector v = (Vector)(gs.getSessionSecurityInf((Q70InformacionSeguridadInterface)contextoSesionUsuario.get(Q70Constantes.SEGURIDAD_USUARIO),"n38login"));
				informacionUsuario.setLogin((String)v.get(0));
			} catch (Exception e) {
				if (-1 != contextoSesionUsuario.getIdContexto().indexOf(es.ejie.frmk.presentacion.base.Q70Constantes.DEFAULT_USER)){
					informacionUsuario.setIdContexto(contextoSesionUsuario.getIdContexto());
					informacionUsuario.setLogin(contextoSesionUsuario.getIdContexto());
				}
				else{
					informacionUsuario.setLogin("ERROR usuario ('"+ contextoSesionUsuario.getIdContexto() +"') en seguridad XL-NETS "+e.getMessage());
				}
			}			 
			listaUsuarios.add(informacionUsuario);
		}
		return listaUsuarios;
	}

	/**
	 * @see es.ejie.frmk.control.monitor.Q70BeanMonitorizacionMBean#getContextoUsuario(java.lang.String)
	 */
	public String getContextoUsuario(String idContexto) {
		Q70ContextoEJIE contextoEJIE = Q70SesionDatos.getSingleton().getContextoGlobal(Q70SesionDatos.CTX_SESION_USUARIO, idContexto);
		return contextoEJIE.toString();
	}

	/**
	 * @see es.ejie.frmk.control.monitor.Q70BeanMonitorizacionMBean#getContextoGlobal(java.lang.String, java.lang.String)
	 */
	public List getContextoGlobal(String tipoContexto, String idContexto) {
		Q70ContextoEJIE contextoEJIE = Q70SesionDatos.getSingleton().getContextoGlobal(tipoContexto, idContexto);

		List contextoGlobal = doObtenerAtributosContexto(contextoEJIE);
		contextoGlobal.add(0, Boolean.valueOf(((Q70ContextoJerarquicoEJIE) contextoEJIE).isMantenible()));

		return contextoGlobal;
	}
	
	/**
	 * @see es.ejie.frmk.control.monitor.Q70BeanMonitorizacionMBean#modificarContextoGlobal(java.lang.String, java.lang.String, java.util.Collection, java.util.Collection)
	 */
	public void modificarContextoGlobal(String tipoContexto, String idContexto, Collection atributos, Collection valores) {
		Q70ContextoJerarquicoEJIE contextoEJIE = (Q70ContextoJerarquicoEJIE)Q70SesionDatos.getSingleton().getContextoGlobal(tipoContexto, idContexto);
		Object[] arrayAtributos = atributos.toArray();
		Object[] arrayValores = valores.toArray();
		for (int i=0;i<arrayAtributos.length;i++)
		{
			Class tipoClase = contextoEJIE.getDynaClass().getDynaProperty((String)arrayAtributos[i]).getType();
			if ( arrayValores[i].equals("") && Number.class.isAssignableFrom(tipoClase)){
				contextoEJIE.setSinNotificacion((String)arrayAtributos[i], null, new ArrayList(1));
			}else{
				contextoEJIE.setSinNotificacion((String)arrayAtributos[i], ConvertUtils.convert((String)arrayValores[i], tipoClase), new ArrayList(1));
			}
		}
	}
	
	/**
	 * @see es.ejie.frmk.control.monitor.Q70BeanMonitorizacionMBean#refrescarContextoGlobal(java.lang.String, java.lang.String)
	 */
	public List refrescarContextoGlobal(String tipoContexto, String idContexto) {
		Q70ContextoEJIE contextoEJIE = Q70SesionDatos.getSingleton().getContextoGlobal(tipoContexto, idContexto);
		Q70SesionDatos.getSingleton().doRefreshContexto(contextoEJIE);		
		return doObtenerAtributosContexto(contextoEJIE);
	}
	
	private ArrayList doObtenerAtributosContexto(Q70ContextoEJIE contextoEJIE) {
		int numPropiedades = contextoEJIE.getDynaClass().getDynaProperties().length;
		ArrayList contextoGlobal = new ArrayList(numPropiedades);
		
		Collection propiedades=new ArrayList(numPropiedades);
		CollectionUtils.addAll(propiedades,contextoEJIE.getDynaClass().getDynaProperties());
		Iterator j = propiedades.iterator();
		while (j.hasNext())
		{
			HashMap property = new HashMap(3);
			String atributo = ((DynaProperty)j.next()).getName();
			property.put("atributo", atributo);
			property.put("valor",contextoEJIE.get(atributo));
			contextoGlobal.add(property);
		}
		return contextoGlobal;
	}
}
