package es.ejie.frmk.control.arranque;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;

import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import javax.naming.NamingException;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

import org.apache.commons.lang.StringUtils;

import weblogic.utils.collections.ArraySet;
import es.ejie.frmk.Q70Factoria;
import es.ejie.frmk.Q70FactoriaConstantes;
import es.ejie.frmk.conectores.conectorJDBC.Q70ConectorJDBC;
import es.ejie.frmk.control.excepciones.Q70FrameworkException;
import es.ejie.frmk.control.excepciones.Q70GestorDeExcepcionesServidor;
import es.ejie.frmk.control.monitor.Q70BeanMonitorizacion;
import es.ejie.frmk.infraestructura.logs.base.Q70ConstantesLogs;
import es.ejie.frmk.infraestructura.logs.base.Q70LogInterno;
import es.ejie.frmk.infraestructura.poolCache.Q70Constantes;
import es.ejie.frmk.infraestructura.poolCache.Q70GestorCacheServidor;
import es.ejie.frmk.infraestructura.poolCache.Q70GestorCacheServidorInterface;
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.infraestructura.utilidades.constantes.Q70UtilidadesConstantes;
import es.ejie.frmk.infraestructura.utilidades.general.Q70UtilidadesGenerales;
import es.ejie.frmk.presentacion.sesiondatos.Q70SesionDatos;

/**
 * Componente que arranca toda la infraestructura necesaria para que Geremua funcione.
 * <p>
 * El <b>Gestor de Arranque y Configuración</b> es un componente básico de Geremua que, 
 * en base a un fichero de configuración, se encarga de arrancar los otros componentes de Geremua. 
 * Estos componentes se denominan componentes arrancables.
 * Los componentes arrancables de tipo Global (1) son los siguientes:
 * <ol>
 * <li>Gestor de Pools y Caché
 * <li>Gestor de Logs
 * <li>Gestor de Trazas
 * <li>Conector JDBC
 * <li>Conector JMS
 * <li>Servicio Coordinacion
 * <li>Gestor de Eventos Remotos
 * <li>Gestor de Tablas de Códigos
 * <li>Gestor de Sesion y Datos
 * <li>Gestor de Apilamiento
 * <li>Gestor de Seguridad
 * <li>Gestor de Mail 
 * <li>Gestor de Sms
 * </ol>
 * <p>
 * Los componentes arrancables de tipo Aplicación (2) son los siguientes:
 * <p>
 * <ol>
 * <li>Gestor de Mensajes e internacionalización
 * <li>Operaciones Internas			
 * <li>Gestor de Excepciones			
 * <li>Gestor de Mapeos
 * <li>Gestor de Pools y Caché
 * <li>Gestor de Logs
 * <li>Gestor de Trazas
 * <li>Gestor de Tablas de Códigos
 * </ol>
 * <p>			
 * Los componentes de Geremua pueden ser de tipo global(1) o de aplicación(2), 
 * según vayan a prestar servicios a todas las aplicaciones corriendo en esa instancia de Geremua, 
 * o sólo a una en concreto. Algunos componentes son de tipo global y de aplicación. 
 * <p>
 * El Gestor de Arranque es responsable de arrancar los dos tipos de componentes.
 * <p>
 * Además, cuándo arranque un componente debe proporcionarle acceso a su configuración.
 * <p>
 * Puesto que este componente arranca a los demás, cabe la cuestión de quién le arranca a él. 
 * El Gestor de Arranque debe arrancar una vez por cada aplicación web que corra en esa instancia de Geremua.
 * <p>
 * El gestor de arranque es también responsable de arrancar la infraestructura <b>JMX</b> 
 * y de proporcionar a Geremua y a las aplicaciones acceso a sus recursos y properties.
 * @version 	2.0
 */
public class Q70GestorArranqueConfiguracionServidor
	implements ServletContextListener,HttpSessionListener, Q70Configurable {


	private static final String COMPONENTE_GLOBAL = "Global";
	private static final String COMPONENTE_DE_APLICACION = "de Aplicacion";
	public static final String ID_COMPONENTE = "GESTOR_ARRANQUE_CONFIGURACION";
	private static final String ARCHIVO_CONFIGURACION_APLICACION = "archivoConfiguracion";
	public static final String ARCHIVO_CONFIGURACION_GLOBAL = "GestorArranqueConfiguracion_global.xml";
	public static final String CODIGO_APLICACION_FRAMEWORK = "q70";
	public static String PATH_CONFIGURACION_GLOBAL = CODIGO_APLICACION_FRAMEWORK + "/";
	private static String PATH_TEST_CASE = "testCase/";	
	//public static final String ID_FRAMEWORK = "idFramework";
	public static final String ID_FRAMEWORK = "GLOBAL_NOMBRE_INSTANCIA";
	private static boolean existeParametroq70entorno = false;
	public static boolean isTestCase = false;
	public static final String LOCK_ACTIVO = "LOCK_ACTIVO";
	public static final String COMPONENTE = "ADMINISTRACION_INSTANCIAS";
	
	public static String Q70_DATASOURCE = "DATA_SOURCE";
	public static String Q70_TABLA_INSTANCIA = "TABLA_INSTANCIAS";
	public static String Q70_CPD_INSTANCIA = "CPD_INSTANCIAS";
	public static final String KEY_PERMISO_ADMINISTRADOR_APLICACION = "APLICACION_CON_PERMISO_ADMINISTRADOR";
	public static final String KEY_PERMISO_ADMINISTRADOR = "PERMISO_ADMINISTRADOR";

	protected static final String WLS_ADMIN_UID_OBJ_XLNETS = "WLS_ADMIN_UID_OBJ_XLNETS";
	protected static final String PASSWORD_SERVIDOR = "PASSWORD_SERVIDOR";
	protected static final String USUARIO_SERVIDOR = "USUARIO_SERVIDOR";
	
	static {
		if (System.getProperty("Q70ENTORNO")!=null) {
			existeParametroq70entorno = true;
			PATH_CONFIGURACION_GLOBAL = System.getProperty("Q70ENTORNO");
			//Concateno la barra al final, de manera que sea opcional ponerla o no al invocar al java -D Q70ENTORNO=...
			if (!PATH_CONFIGURACION_GLOBAL.endsWith("/") && !PATH_CONFIGURACION_GLOBAL.endsWith("\\"))
				PATH_CONFIGURACION_GLOBAL+="/";
		}
	}
	
	public static final String ARCHIVO_PROPERTIES_GLOBAL = "q70.properties";
	public static final String  PREFIJO_APLICACION_DENTRO_WAR = "/WEB-INF/aplic/";
	public static final String SUFIJO_APLICACION = "/exe/";
	
	public static final String NOMBRE_APLICACION = "aplicacion";
	public static final String STR_ID_APLICACION_GLOBAL = "idContextoAplicacionGlobal";
	public static final String PATH_DOMINIO_CLUSTER = "pathDominioCluster";

	public static final String PROPERTY_PATH_TEST_CASE = "PATH_TEST_CASE";
	public static final String PROPERTY_PATH_LOGS_TEST_CASE = "PATH_LOGS_TEST_CASE";
		
	private static ArrayList nombreComponentesGlobales = new ArrayList();
	private static ArrayList claseComponentesGlobales = new ArrayList();
	private static ArrayList configuracionComponentesGlobales = new ArrayList();
	private static ArrayList claseMBeanGlobal = new ArrayList();
	private static ArrayList claseMBeanGlobalAnterior = null;
	private static ArrayList clasePersistenciaGlobal = new ArrayList();

	private static MBeanServer mBeanServerDefault = null;
	private static NotificationListener instanciaListenerMBean = null;
	private static Q70GestorMBeanServer gestorMBeanServer;
	
	private ArrayList nombreComponentesAplicacion = new ArrayList();
	private ArrayList claseComponentesAplicacion = new ArrayList();
	private ArrayList configuracionComponentesAplicacion = new ArrayList();
	private ArrayList claseMBeanAplicacion = new ArrayList();
	private ArrayList claseMBeanAplicacionAnterior = null;
	private ArrayList clasePersistenciaAplicacion = new ArrayList();
	private String nombreGestorMBeanServer = null;
	private HashMap atributosMBeanServer = new HashMap();
	
	private static ArraySet aplicacionesDominioCluster = new ArraySet();
	private static String pathDominioCluster = null;
	
	private String nombreComponente = null;
	private String nombreClase = null;
	private String ficheroConfiguracion = null;
	private String tipoComponente = null;
	private String nombreClaseMBean = null;
	private String nombreClasePersistencia = null;
	
	private boolean esDespliegueGlobal = false;
	
	private static Q70ClassLoaderMap classLoaderMap = Q70ClassLoaderMap.getInstance();
		
	/**
	 * Inicializar la infraestructura de una aplicación web.
	 * <p>
	 * Se ejecuta al iniciarse el servidor, que lee de su fichero
	 * de configuración qué componentes de Geremua son arrancables, crea
	 * estos componentes y hace que sean configurados, y guarda una lista
	 * de estos componentes. 
	 */
	public void contextInitialized(ServletContextEvent arg0) {

		Q70LogInterno.log("GestorArranqueConfiguracion --> contextInitialized");

		// Si se esta en medio de un testcase Junit, hay que esperar que este acabe para que se pueda desplegar una apliacion
		if (isTestCase){
			Date fechaHora = new Date();
			String mensajeError = "No ha sido posible deplegar.\n";
			mensajeError += "*********************************************************\n";
			mensajeError += "***       EXCEPCION (" + fechaHora + ")   ***\n";
			mensajeError += "*** No se ha podido desplegar la aplicacion "+ arg0.getServletContext().getInitParameter(
					NOMBRE_APLICACION) +",      ***\n";
			mensajeError += "*** porque Geremua está ejecutando un test Junit...   ***\n";
			mensajeError += "*** Hay que esperar a que acabe la ejecucion de los   ***\n";
			mensajeError += "*** test para poder desplegar la aplicacion.          ***\n";
			mensajeError += "*********************************************************\n";
			throw new RuntimeException(mensajeError);
		}
		
		//Introducir la referencia del ServletContext en la hash de Contextos de aplicacion
		try {
			classLoaderMap.setServletContext(arg0.getServletContext());
		} catch (Q70FrameworkException e2) {
			doTrazaError(e2, "Error al introducir la referencia del ServletContext");
		}
		setAttribute(ID_COMPONENTE, this);

		//Leyendo la configuracion acerca de componentes globales arrancables
		if (classLoaderMap.size() == 1)
		{
			Q70LogInterno.log("GestorArranqueConfiguracion(contextInitialized): Inicializando los componentes de servidor de Geremua.");	
			tipoComponente = COMPONENTE_GLOBAL;
			InputStream fisGlobal =
				getResourceAsStream(PATH_CONFIGURACION_GLOBAL + ARCHIVO_CONFIGURACION_GLOBAL);
			Q70Configurador configuradorGlobal = new Q70Configurador(this, fisGlobal);
			try
			{
				esDespliegueGlobal = true;
				configuradorGlobal.generateConfiguration();
			} 
			catch (Exception e1) {
				doTrazaError(e1, "Error al leer la configuración de componentes globales del gestor de arranque y configuración");
			}
			esDespliegueGlobal = false;
			
			try{
				prepararInfraestructuraJMX();
			}catch (Throwable e){
				doTrazaError(e, "Error al preparar la infraestructura JMX.");
			}
		}
		
		//******************************************************
		if ("true".equals(arg0.getServletContext().getInitParameter(PATH_DOMINIO_CLUSTER))){
			aplicacionesDominioCluster.add(arg0.getServletContext().getInitParameter(NOMBRE_APLICACION));
		}
		//******************************************************
		
		//Leyendo la configuracion acerca de componentes de aplicacion arrancables
		tipoComponente = "Aplicacion";
		InputStream fisAplicacion =
			getApplicationConfigAsStream(arg0.getServletContext().getInitParameter(
			ARCHIVO_CONFIGURACION_APLICACION));
		Q70Configurador configuradorAplicacion = new Q70Configurador(this, fisAplicacion);
		try
		{
			configuradorAplicacion.generateConfiguration();
		} 
		catch (Exception e1) {
			doTrazaError(e1, "Error al leer la configuración de componentes de aplicacion del gestor de arranque y configuración");
		}

		String nombreClase = "";
		//Arrancando componentes globales
		if (classLoaderMap.size() == 1)
		{
			for (int i = 0; i < claseComponentesGlobales.size(); i++)
			{
				try{
					nombreClase = (String) claseComponentesGlobales.get(i);
					String pathFicheroXml = PATH_CONFIGURACION_GLOBAL + (String)configuracionComponentesGlobales.get(i);
					doArrancarComponente(COMPONENTE_GLOBAL, nombreClase, pathFicheroXml, (String)claseMBeanGlobal.get(i), (String)clasePersistenciaGlobal.get(i),CODIGO_APLICACION_FRAMEWORK);
				}catch (Throwable e){
					doTrazaError(e, "Error al configurar los componentes globales del gestor de arranque y configuración");
				}
			}
				
			try{
				doGrabarInstanciaLevantada();
			}catch (Throwable e){
				doTrazaError(e, "Error al grabar en la Tabla de instancias.");
			}
		}
		String aplicacion = arg0.getServletContext().getInitParameter(NOMBRE_APLICACION);
		Q70Factoria.setAplicacionCode(aplicacion);
		
		//Arrancando componentes de aplicacion
		for (int i = 0; i < claseComponentesAplicacion.size();i++)
		{
			try
			{
				nombreClase = (String) claseComponentesAplicacion.get(i);
				String pathFicheroXml = (String)configuracionComponentesAplicacion.get(i);	
				doArrancarComponente(COMPONENTE_DE_APLICACION, nombreClase, pathFicheroXml, (String)claseMBeanAplicacion.get(i), (String)clasePersistenciaAplicacion.get(i),Q70Factoria.getAplicacionCode());
			}catch (Throwable e){
				doTrazaError(e, "Error en aplicación:'"+ aplicacion +"', al configurar los componentes de aplicacion del gestor de arranque y configuración");
			}
		}
		//Archivado de instancia de Gestor de Arranque y Configuracion en ServletContext
		arg0.getServletContext().setAttribute(ID_COMPONENTE,this);
		//Preparación Contexto Global de Aplicación
		try
			{
			String strAplicacionGlobal = arg0.getServletContext().getInitParameter(STR_ID_APLICACION_GLOBAL);
			Q70SesionDatos.getSingleton().crearContextoGlobalAplicacion(strAplicacionGlobal,"contextoGlobalAplicacion_"+strAplicacionGlobal+".xml");
		}
		catch(Throwable t)
		{
			doTrazaError(t, "Error en aplicación:'"+ aplicacion +"', al crear el contexto global de aplicacion en el gestor de arranque y configuración");
		}
		String beanMonitorizacionAplicacion = "";
		//Registrar el bean de monitorización si lo hay
		try {
			beanMonitorizacionAplicacion =(String)arg0.getServletContext().getInitParameter("beanMonitorizacion");
			Q70LogInterno.log("Bean de monitorización suministrado por la aplicación " + aplicacion + ": " +beanMonitorizacionAplicacion);
			if (beanMonitorizacionAplicacion!=null) { 
				Object instancia = cargarClase(beanMonitorizacionAplicacion).newInstance();
				instrumentarYRegistrar(instancia, Q70Factoria.getAplicacionCode(),false);
			}
		}
		catch(Throwable t) {
			doTrazaError(t, "Error en aplicación:'"+ aplicacion +"', al crear el bean de monitorizacion de una aplicacion en el gestor de arranque y configuración");
		}
	}

	private static String doGetPathConfigAplicacion(String codigoAP) {
		if (aplicacionesDominioCluster.contains(codigoAP)){
			// Calcular path '/dominio/cluster/'
			if (null == pathDominioCluster) {
				pathDominioCluster = "/" + gestorMBeanServer.getNombreDominio() + "/" + ("".equals(gestorMBeanServer.getNombreClusterInstancia())?gestorMBeanServer.getIdInstancia():gestorMBeanServer.getNombreClusterInstancia()) + "/";
			}
			return pathDominioCluster;
		}
		return "/";
	}

	private void doArrancarComponente(String strTipoComponente, String strNombreClase, String strPathFicheroXml, String strNombreClaseMBean, String strNombreClasePersistencia, String strCodAplicacion) throws Exception {
		Class clase = cargarClase(strNombreClase);
		
		Q70LogInterno.log("Arrancando componente "+ strTipoComponente +": " + strNombreClase);
		if (null!=strNombreClaseMBean){
			try{
				invokeMBean(mBeanServerDefault, strNombreClaseMBean, strCodAplicacion, "incrementarContador", new Object[0], new String[0]);
			}catch (InstanceNotFoundException e) {
				try {
					Class claseMBean = cargarClase(strNombreClaseMBean);
					Q70MBeanInterface componente =	instanciarClaseMBean(claseMBean);
					instrumentarYRegistrar(componente,strCodAplicacion,true);
					componente.setNombreComponente(strNombreClase);
					componente.setNombreConfiguracion(strNombreClasePersistencia);
					if (COMPONENTE_DE_APLICACION.equals(strTipoComponente)){
						strPathFicheroXml = getPathAplicacion(strPathFicheroXml);
					}
					componente.afterCreate(strPathFicheroXml);
				} catch (Throwable t) {
					Q70TrazaInterface traza = Q70Factoria.getTraza();
					String strMensaje = "tipoComponente:'"+strTipoComponente+"',nombreClase:'"+strNombreClase+"',pathFicheroXml:'"+strPathFicheroXml+"',nombreClaseMBean:'"+strNombreClaseMBean+"',nombreClasePersistencia:'"+strNombreClasePersistencia+"',codAplicacion:'"+strCodAplicacion+"'";
					if (traza.isTraceActive()){
						traza.trace(ID_COMPONENTE, Q70TraceLevel.ERROR, "Error en doArrancarComponente:"+strMensaje);
					}
					throw new Q70FrameworkException(ID_COMPONENTE,this.getClass().toString(),"doArrancarComponente","Componente-->"+strMensaje,t);
				}
			}
		}else{
			Q70Arrancable componente =	(Q70Arrancable) clase.getMethod("getSingleton", null).invoke(null,null);							
			componente.afterCreate(strPathFicheroXml);
		}
		Q70LogInterno.log("Componente "+ strTipoComponente + strNombreClase + " arrancado con éxito");
	}

	private void unRegistrarMBean(String nombreClaseMBean, String codAplicacion) throws Exception{
		Set listaNombres = mBeanServerDefault.queryNames(Q70MBeanBase.getNombreMBean(nombreClaseMBean,codAplicacion),null);
		if (listaNombres.isEmpty()){
			throw new InstanceNotFoundException(Q70MBeanBase.getNombreMBean(nombreClaseMBean,codAplicacion).toString());
		}else{
			if (listaNombres.size()>1){
				throw new InstanceAlreadyExistsException(Q70MBeanBase.getNombreMBean(nombreClaseMBean,codAplicacion).toString());
			}else{
				unRegistrarComponente((ObjectName)listaNombres.iterator().next());
			}
		}
	}

	protected Q70MBeanInterface instanciarClaseMBean(Class claseMBean) throws InstantiationException, IllegalAccessException {
		return (Q70MBeanInterface) claseMBean.newInstance();
	}
	
	protected Class cargarClase(String nombreClase) throws Exception {
		return Class.forName(nombreClase);
	}

	private static Class cargarClaseDesdeServidor(String nombreClase) throws Exception {
		return Class.forName(nombreClase, true, Thread.currentThread().getContextClassLoader());
	}

	/**
	 * Volver a configurar un componente en caliente (sin parar el servidor de aplicaciones)
	 * @param nombreComponente
	 * @return
	 */

	public static boolean reconfigurarComponente(String nombreComponente, String xmlConfiguracion) throws Q70FrameworkException{
		if (nombreComponente.equals("gestorArranqueConfiguracion")){
			return true;
		}
		int indice = nombreComponentesGlobales.indexOf(nombreComponente);
		if (indice<0)
			return false;
		else {
			try {
				escribirFichero(xmlConfiguracion, (String)configuracionComponentesGlobales.get(indice), CODIGO_APLICACION_FRAMEWORK);
				
				String nombreClase = (String)claseComponentesGlobales.get(indice);
				Class clase = cargarClaseDesdeServidor(nombreClase);
				Q70LogInterno.log("Reconfigurando componente Global: " + nombreClase);
				Q70Arrancable componente =	(Q70Arrancable) clase.getMethod("getSingleton", null).invoke(null,null);
				synchronized(componente) {
					//componente.afterCreate(PATH_CONFIGURACION_GLOBAL + configuracionComponentesGlobales.get(indice));
					componente.reConfigure();
				}
				Q70LogInterno.log("Componente Global" + nombreClase + " reconfigurado con éxito");
				return true;
			}
			catch (Exception e) {
				Q70LogInterno.log("No se ha podido reconfigurar "+nombreComponente+" "+e.getMessage());
				throw new Q70FrameworkException(ID_COMPONENTE,Q70GestorArranqueConfiguracionServidor.class.getName(),"reconfigurarComponente","No se ha podido reconfigurar "+nombreComponente+" "+e.getMessage(),e);
			}
		}
	}

	/**
	 * Método que actualiza un fichero (de configuración o de propiedades)determinado de una aplicación concreta. 
	 * Se le pasa como parámetro el contenido con el que queremos actualizar el fichero, el nombre del fichero 
	 * y la aplicación a la que pertenece.
	 * @param contenidoFichero
	 * @param fichero
	 * @param codAplicacion
	 * @return
	 */
	private static void escribirFichero(String contenidoFichero, String fichero, String codAplicacion) throws IOException
	{
		OutputStreamWriter ficheroWriter = null;
		String pathEscritura = null;
		if ((codAplicacion != null) && (codAplicacion.equals(CODIGO_APLICACION_FRAMEWORK))){
			pathEscritura = getGlobalProperty("PREFIJO_PATH_CONFIGURACION_GLOBAL_ABSOLUTO") + "/" + PATH_CONFIGURACION_GLOBAL;
			ficheroWriter = new OutputStreamWriter(new FileOutputStream(pathEscritura + fichero),Q70UtilidadesConstantes.ENCODING);
		}else {
			pathEscritura = getGlobalProperty("PREFIJO_PATH_CONFIGURACION_GLOBAL_ABSOLUTO") + "/" + codAplicacion + doGetPathConfigAplicacion(codAplicacion) + fichero;
			ficheroWriter = new OutputStreamWriter(new FileOutputStream(pathEscritura),Q70UtilidadesConstantes.ENCODING);
		}
		ficheroWriter.write(contenidoFichero);
		ficheroWriter.close();
	}
	
	/**
	 * Método de reconfiguración de un fichero properties de una aplicación determinada. Invoca a un método de escritura
	 * sobre un fichero determinado ubicado en un path concreto.
	 * @param codAplicacion
	 * @param contenidoFichero
	 * @return
	 */
	public static boolean reconfigurarFicheroPropiedades (String codAplicacion, String contenidoFichero) throws Q70FrameworkException {
		try {			
			escribirFichero (contenidoFichero, codAplicacion + ".properties", codAplicacion);
			// Liberamos la caché de properties de la aplicación.
			String idCacheProperties = Q70Constantes.CACHE_PROPERTIES; 
			if (codAplicacion.equalsIgnoreCase(CODIGO_APLICACION_FRAMEWORK)) {
				idCacheProperties = Q70Constantes.CACHE_Q70_PROPERTIES;
			}
			invokeMBeanPorTipoComponenteAplicacion(Q70FactoriaConstantes.ID_GESTOR_CACHE,codAplicacion,"freeCache", new Object[]{idCacheProperties}, new String[]{"java.lang.String"});
		}catch (Exception e) {
			Q70LogInterno.log("No se ha podido reconfigurar el fichero de propiedades de la aplicación: "+codAplicacion+" "+e.getMessage());
			return false;
		}
		return true;
	}
	

	/**
	 * Ejecutar acciones para detener Geremua de forma ordenada.
	 * <p> 
	 * Se ejecuta antes de que el servidor sea detenido.
	 * Se comunica a cada uno de los componentes arrancables de
	 * la lista guardada al iniciarse el servidor que realicen las
	 * tareas de finalización pertinentes. 
	 */
	public void contextDestroyed(ServletContextEvent arg0) {
		
		Q70LogInterno.log("GestorArranqueConfiguracion --> contextDestroyed");
		//Comunicar a los componentes arrancables que realicen las tareas
		//prefinalización del servidor.
		Class clase = null;
		Q70Arrancable componente = null;
		
		//Componentes Aplicacion
		for (int i = claseComponentesAplicacion.size()-1; i >= 0; i--)
		{
			try
			{
				if (null!=(String)claseMBeanAplicacion.get(i)){
					invokeMBean(mBeanServerDefault,(String)claseMBeanAplicacion.get(i), Q70Factoria.getAplicacionCode(), "decrementarContador",new Object[0], new String[0]);
					if (((Boolean) invokeMBean(mBeanServerDefault,(String)claseMBeanAplicacion.get(i), Q70Factoria.getAplicacionCode(), "esContadorCero", new Object[0], new String[0])).booleanValue())	
						beforeDestroyMBean((String)claseMBeanAplicacion.get(i),Q70Factoria.getAplicacionCode());
				}else{	
					clase = cargarClase((String)claseComponentesAplicacion.get(i)); 
					componente = (Q70Arrancable) clase.getMethod("getSingleton", null).invoke(null,null);
					Q70LogInterno.log("Destruyendo componente de Aplicacion: " + clase);
					componente.beforeDestroy();
					Q70LogInterno.log("Destruyendo componente de Aplicacion: " + clase);
				}
			}catch (Throwable e)
			{
				Q70GestorDeExcepcionesServidor.getSingleton().recogerThrowable(new Q70FrameworkException(ID_COMPONENTE,this.getClass().toString(),"contextDestroyed","Se ha producido un error en el método contextDestroyed al destruir un componente de aplicación.",e));
			}
		}

		//DeRegistrar el bean de monitorización de la aplicacion si lo hay
		try {
			String beanMonitorizacionAplicacion =(String)arg0.getServletContext().getInitParameter("beanMonitorizacion");
			if (beanMonitorizacionAplicacion!=null) { 
				unRegistrarComponente(new ObjectName(getObjectName(beanMonitorizacionAplicacion,Q70Factoria.getAplicacionCode())));
			}
		}
		catch(Throwable t) {
			Q70GestorDeExcepcionesServidor.getSingleton().recogerThrowable(new Q70FrameworkException(ID_COMPONENTE,this.getClass().toString(),"contextDestroyed","Se ha producido un error en el método contextDestroyed al 'deregistrar' un bean de aplicación.",t));
		}
		
		Q70Factoria.removeAplicacion();
		
		//Componentes Globales
		if (classLoaderMap.size() == 1)
		{
			try {
				doBorrarInstanciaLevantada();
			} catch (Q70FrameworkException e) {
				Q70GestorDeExcepcionesServidor.getSingleton().recogerThrowable(new Q70FrameworkException(ID_COMPONENTE,this.getClass().toString(),"contextDestroyed","Se ha producido un error en el método contextDestroyed al borrar la instancia de la Tabla de instancias.",e));
			}
			
			for (int i = claseComponentesGlobales.size()-1; i >=0; i--)
			{
				try
				{
					if (null!=(String)claseMBeanGlobal.get(i)){
						beforeDestroyMBean((String)claseMBeanGlobal.get(i),CODIGO_APLICACION_FRAMEWORK);
					}
					else{
						clase = cargarClase((String)claseComponentesGlobales.get(i));
						componente = (Q70Arrancable) clase.getMethod("getSingleton", null).invoke(null,null);
						Q70LogInterno.log("Destruyendo componente Global: " + clase);							
						componente.beforeDestroy();
						Q70LogInterno.log("Destruyendo componente Global: " + clase);
					}
				}
				catch (Throwable e)
				{
					Q70GestorDeExcepcionesServidor.getSingleton().recogerThrowable(new Q70FrameworkException(ID_COMPONENTE,this.getClass().toString(),"contextDestroyed","Se ha producido un error en el método contextDestroyed al destruir un componente global.",e));				
				}
			}
			claseComponentesGlobales.clear();
			//DeRegistrar el bean de monitorización de q70
			try {
				unRegistrarComponente(new ObjectName(getObjectName(Q70BeanMonitorizacion.class.getName(),CODIGO_APLICACION_FRAMEWORK)));
			}
			catch(Throwable t) {
				Q70GestorDeExcepcionesServidor.getSingleton().recogerThrowable(new Q70FrameworkException(ID_COMPONENTE,this.getClass().toString(),"contextDestroyed","Se ha producido un error en el método contextDestroyed al 'deregistrar' el bean de monitorización de Q70.",t));
			}
		}		
		//Finalizando contexto
		try {
			classLoaderMap.removeServletContext();
		} catch (Q70FrameworkException e) {
			Q70GestorDeExcepcionesServidor.getSingleton().recogerThrowable(new Q70FrameworkException(ID_COMPONENTE,this.getClass().toString(),"contextDestroyed","Error al eliminar la referencia del ServletContext",e));
		}
	}

	/**
	 * Reconfigurar los componentes de Geremua para ejecutar los casos de prueba.
	 */
	public void reconfigureForTestCase() throws Q70FrameworkException{
		try {
			String pathTestCase = getGlobalProperty(PROPERTY_PATH_TEST_CASE);
			String pathLogsTestCase = getGlobalProperty(PROPERTY_PATH_LOGS_TEST_CASE);
			File directorio = null;
			if(pathTestCase != null){
				directorio = new File(pathTestCase);
				if(!directorio.exists())
					if(directorio.mkdirs())
						Q70LogInterno.log("Creado directorio " + pathTestCase);
			}
			if(pathLogsTestCase != null)
			{
				directorio = new File(pathLogsTestCase);
				if(!directorio.exists())
					if(directorio.mkdirs())			
						Q70LogInterno.log("Creado directorio " + pathLogsTestCase);
			}
			isTestCase = true;
			reconfigureAllComponents(PATH_TEST_CASE);
		} catch (Throwable e) {
			Q70TrazaInterface traza = Q70Factoria.getTraza();
			if (traza.isTraceActive()){
				traza.trace(ID_COMPONENTE, Q70TraceLevel.ERROR, "Se ha producido un error en el método reconfigureForTestCase");
			}
			throw new Q70FrameworkException(ID_COMPONENTE,this.getClass().toString(),"reconfigureForTestCase","Se ha producido un error en el método reconfigureForTestCase.",e);
		}
	}
	
	/**
	 * Reconfigurar los componentes de Geremua después de ejecutar los casos de prueba.
	 */
	public void reconfigureAfterTestCase() throws Q70FrameworkException{
		try {
			reconfigureAllComponents("");
			isTestCase = false;
		} catch (Q70FrameworkException e) {
			Q70TrazaInterface traza = Q70Factoria.getTraza();
			if (traza.isTraceActive()){
				traza.trace(ID_COMPONENTE, Q70TraceLevel.ERROR, "Se ha producido un error en el método reconfigureAfterTestCase");
			}
			throw new Q70FrameworkException(ID_COMPONENTE,this.getClass().toString(),"reconfigureAfterTestCase","Se ha producido un error en el método reconfigureAfterTestCase.",e);
		}
	}
	
	/**
	 * Reconfigurar los componentes de Geremua con ficheros en un path relativo
	 */
	private void reconfigureAllComponents(String aPath) throws Q70FrameworkException{
		// guardo los mbeans anteriores para destruir luego los que no se necesitan
		claseMBeanGlobalAnterior = (ArrayList)claseMBeanGlobal.clone();
		claseMBeanAplicacionAnterior = (ArrayList)claseMBeanAplicacion.clone(); 
		
		//Borro la configuración anterior
		nombreComponentesGlobales.clear();
		claseComponentesGlobales.clear();
		configuracionComponentesGlobales.clear();
		claseMBeanGlobal.clear();
		clasePersistenciaGlobal.clear();

		nombreComponentesAplicacion.clear();
		claseComponentesAplicacion.clear();
		configuracionComponentesAplicacion.clear();		
		claseMBeanAplicacion.clear();
		clasePersistenciaAplicacion.clear();
		
		//Reconfiguración de Componentes globales
		tipoComponente = COMPONENTE_GLOBAL;
		InputStream fisGlobal =
			getResourceAsStream(PATH_CONFIGURACION_GLOBAL + aPath + ARCHIVO_CONFIGURACION_GLOBAL);
			Q70Configurador configuradorGlobal = new Q70Configurador(this, fisGlobal);
		try
		{
			configuradorGlobal.generateConfiguration();
		} 
		catch (Exception e1) {

			if (Q70LogInterno.isEnabled())
				Q70LogInterno.log(
					"Error al leer la configuración de componentes globales del gestor de arranque y configuración"
						+ "\n "
						+ e1.getLocalizedMessage());
			throw new Q70FrameworkException(ID_COMPONENTE,this.getClass().toString(),"reconfigureAllComponents","Se ha producido un error en el método reconfigureAllComponents al leer la configuración de componentes globales .",e1);
		}
		//Leyendo la configuracion acerca de componentes de aplicacion arrancables
		tipoComponente = "Aplicacion";
		InputStream fisAplicacion =
			getApplicationConfigAsStream("/" + aPath + classLoaderMap.getServletContext().getInitParameter(ARCHIVO_CONFIGURACION_APLICACION));

		Q70Configurador configuradorAplicacion = new Q70Configurador(this, fisAplicacion);
		try
		{
			configuradorAplicacion.generateConfiguration();
		} 
		catch (Exception e1) {

			if (Q70LogInterno.isEnabled())
				Q70LogInterno.log(
					"Error al leer la configuración de componentes de aplicacion del gestor de arranque y configuración"
						+ "\n "
						+ e1.getLocalizedMessage());
			throw new Q70FrameworkException(ID_COMPONENTE,this.getClass().toString(),"reconfigureAllComponents","Se ha producido un error en el método reconfigureAllComponents al leer la configuración de componentes de aplicacion.",e1);
		}

		//Destruir los mbean globales sobrantes
		for (int i = 0; i < claseMBeanGlobalAnterior.size(); i++){
			try {
				if (!claseMBeanGlobal.contains(claseMBeanGlobalAnterior.get(i))){
					beforeDestroyMBean((String)claseMBeanGlobalAnterior.get(i),CODIGO_APLICACION_FRAMEWORK);
				}
			} catch (Throwable e1) {
				doTrazaError(e1, "Se ha producido un error en el método reconfigureAllComponents, al destruir los mbean Globales sobrantes");
			}
		}
		//Destruir los mbean de aplicacion sobrantes
		for (int i = 0; i < claseMBeanAplicacionAnterior.size(); i++){
			try {
				if (!claseMBeanAplicacion.contains(claseMBeanAplicacionAnterior.get(i))){
					beforeDestroyMBean((String)claseMBeanAplicacionAnterior.get(i),Q70Factoria.getAplicacionCode());
				}
			} catch (Throwable e1) {
				doTrazaError(e1, "Se ha producido un error en el método reconfigureAllComponents, al destruir los mbean de Aplicacion sobrantes");
			}
		}

		String nombreClase = "";
		String path = "";

		//Reconfigurando componentes globales
		for (int i = 0; i < claseComponentesGlobales.size(); i++)
		{
			try
			{
				path = PATH_CONFIGURACION_GLOBAL + aPath + configuracionComponentesGlobales.get(i);
				if (null!=(String)claseMBeanGlobal.get(i)){
					reconfigureComponenteMBean(COMPONENTE_GLOBAL, (String) claseComponentesGlobales.get(i), path, (String)claseMBeanGlobal.get(i),(String)clasePersistenciaGlobal.get(i),CODIGO_APLICACION_FRAMEWORK);
				}
				else{
					nombreClase = (String) claseComponentesGlobales.get(i);
					// Bean Monitorizacion
					Class clase = cargarClase(nombreClase);
					Q70LogInterno.log("Arrancando componente Global: " + nombreClase);
					Q70Arrancable componente =	(Q70Arrancable) clase.getMethod("getSingleton", null).invoke(null,null);
					componente.beforeDestroy();
					componente.afterCreate(path);
				}
			}
			catch (Throwable e)
			{
				doTrazaError(e, "Se ha producido un error en el método reconfigureAllComponents, al reconfigurar los componentes globales.");
			}
		}
		//Arrancando componentes de aplicacion
		for (int i = 0; i < claseComponentesAplicacion.size();i++)
		{
			try
			{
				path = aPath + (String) configuracionComponentesAplicacion.get(i);
				if (null!=(String)claseMBeanAplicacion.get(i)){
					reconfigureComponenteMBean(COMPONENTE_DE_APLICACION, (String) claseComponentesAplicacion.get(i), path, (String)claseMBeanAplicacion.get(i), (String) clasePersistenciaAplicacion.get(i), Q70Factoria.getAplicacionCode());
				}
				else{
					nombreClase = (String) claseComponentesAplicacion.get(i);
					// Bean Monitorizacion
					Class clase = cargarClase(nombreClase);
					Q70LogInterno.log("Arrancando componente de Aplicacion: " + nombreClase);
					Q70Arrancable componente =	(Q70Arrancable) clase.getMethod("getSingleton", null).invoke(null,null);
					componente.beforeDestroy();							
					componente.afterCreate(path);
				}
			}
			catch (Throwable e)
			{
				doTrazaError(e, "Se ha producido un error en el método reconfigureAllComponents, al reconfigurar los componentes de Aplicacion.");
			}
		}
	}	

	/**
	 * Leer un recurso de Geremua
	 * @param path
	 * @return
	 */
	public static InputStream getGlobalResourceAsStream(String path) {
		if (path!=null && path.startsWith("/"))
			path=path.substring(1);
		String pathCompleto = PATH_CONFIGURACION_GLOBAL+path;
		InputStream is = ClassLoader.getSystemResourceAsStream(pathCompleto);
		return is;
	}
		
	/**
	 * Leer recurso.
	 * <p>
	 * Lee recursos tanto globales como de aplicación indicando el path del recurso
	 * @param path
	 * @return
	 */
	public static InputStream getResourceAsStream(String path) {
		InputStream is = null;
		try {
			is = classLoaderMap.getServletContext().getResourceAsStream(path);
			//Si el resultado es nulo intentar cargarlo con el classloader del thread;
			if (is == null)
				throw new Exception();
		}
		//Si se produce algún error intentar realizar la carga con el classloader del thread.
		catch (Throwable e) {
			is = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
		}
		return is;
	}

	/**
	 * Para leer atributos o componentes del Servlet Context apropiado
	 * @param componente
	 * @return
	 */
	public static Object getAttribute(String componente) {
		
		try {
			return classLoaderMap.getServletContext().getAttribute(componente);
		} catch (Q70FrameworkException e) {
			Q70GestorDeExcepcionesServidor.getSingleton().recogerThrowable(e);
		}
		return null;
	}

	/**
	 * Para dar valor a un atributo o componente en el Servlet Context apropiado 
	 */	
	public static void setAttribute(
		
		String nombreComponente,
		Object componente) {
		try {
			classLoaderMap.getServletContext().setAttribute(
				nombreComponente,
				componente);
		} catch (Q70FrameworkException e) {
			Q70GestorDeExcepcionesServidor.getSingleton().recogerThrowable(e);
		}
	}
		
	
	/**
	 * Método encargado de la recuperación del contenido de un fichero de configuración
	 * @param nombreComponente
	 * @return
	 * @throws Q70FrameworkException
	 */
	public static String getConfiguracionComponente(String nombreComponente) throws Q70FrameworkException
	{	
		String path = null;
		if (nombreComponente.equalsIgnoreCase("gestorArranqueConfiguracion"))
		{
			path = ARCHIVO_CONFIGURACION_GLOBAL;
		} else {
			path = pathConfiguracionComponente(nombreComponente);
		}		
		return getContenidoFichero(path, CODIGO_APLICACION_FRAMEWORK);		
	}
	
	
	/**
	 * Método que en base a un código de aplicación concreto, monta el path en el que se ubica el
	 * fichero de propiedades de esa aplicación para recuperar su contenido.
	 * @param codigoAplicacion del fichero de propiedades cuyo contenido deseamos recuperar
	 * @return
	 * @throws Q70FrameworkException
	 */
	public static String getContenidoFicheroPropiedades(String codigoAplicacion) throws Q70FrameworkException
	{	
		String ficheroProperties = codigoAplicacion + ".properties";
		return getContenidoFichero(ficheroProperties , codigoAplicacion);		
	}

	/**
	 * Método para la recuperación del contenido de un fichero de propiedades concreto. Se puede tratar de un
	 * fichero de propiedades de una instancia de Geremua (q70.properties) o de un fichero de propiedades de
	 * alguna de las aplicaciones desplegadas bajo una instancia.
	 * @param path en el que se encuentra ubicado el fichero.properties
	 * @param codigoAplicacion del fichero de propiedades cuyo contenido deseamos recuperar
	 * @return
	 * @throws Q70FrameworkException
	 */
	private static String getContenidoFichero(String path, String codigoAplicacion) throws Q70FrameworkException
	{
		InputStream is = null;
		if ((codigoAplicacion != null) && (codigoAplicacion.equals(CODIGO_APLICACION_FRAMEWORK))){
			is = getGlobalResourceAsStream(path);
		}else {
			is = getApplicationConfigFile(path, codigoAplicacion);
		}
		String resultado = null;
		try {
			resultado = Q70UtilidadesGenerales.readInputStream(is);
		} 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 getContenidoConfiguracion");
			}
			throw new Q70FrameworkException(ID_COMPONENTE,Q70GestorArranqueConfiguracionServidor.class.getName(),"getContenidoConfiguracion","Se ha producido un error en el método getContenidoConfiguracion.",e);
		}
		return resultado;
	}

	/**
	 * Método que gestiona la persistencia de la configuración de Logs y Trazas. Para ello se tiene en cuenta
	 * la configuración original del fichero de configuración y los pares atributo - valor con los valores
	 * que hayan sido actualizados por pantalla. De esta forma, teniendo en cuenta la parte de la configuración
	 * que no se ha modificado y la que sí ha sido actualizada, se monta la configuración final que será la que 
	 * se envíe a la persistencia para la actualización del fichero de configuración.
	 * 
	 * @param listaInstancias lista de instancias disponibles
	 * @param instanciaSeleccionada aquella instancia de Geremua en la que se encuentra el componente a reconfigurar
	 * @param tipo MBean del componente que sobre el que se va a realizar la persistencia (logs, trazas, caché,...)
	 * @param codAplicacion código de la aplicación del componente 
	 * @param subComponente subtipo de componente (LOG_ACCESOS, LOG_NAVEGACION, LOG_INCIDENCIAS, TRACER,...)
	 * @param atributos pares atributo - valor que conforman la configuración del componente
	 * 
	 * @return
	 * 
	 * @throws Q70FrameworkException
	 */
	public static Object persisteConfiguracionXMLTrazaLogs (List listaInstancias, Q70Instancia instanciaSeleccionada, String tipo, String codAplicacion, String subComponente, HashMap atributos) throws Q70FrameworkException{
		Object resultado = null;
		try {
			MBeanServer mBeanserver = getInstancia(instanciaSeleccionada);
			String configuracionOriginal = recuperaConfiguracionXML(mBeanserver, tipo, codAplicacion);
				
			Set claves = atributos.keySet();
			Iterator iterator = claves.iterator();
			
			int indiceLogRemoto = configuracionOriginal.indexOf(Q70ConstantesLogs.LOG_REMOTO_ACTIVO);
			String configuracionInicialLogRemoto = configuracionOriginal.substring(0, indiceLogRemoto);
			String configuracionFinalLogRemoto = configuracionOriginal.substring(indiceLogRemoto);
				
			int indiceSubComponente = configuracionOriginal.indexOf(subComponente);
			String configuracionFinal = configuracionOriginal.substring(indiceSubComponente);
				
			String clave = null;
			String valor = null;
			int indiceClave = 0;
			int indiceValor = 0;
			int indiceFinValor = 0;
				
			while (iterator.hasNext()) {
				clave = (String)iterator.next();
				if ((clave.equals(Q70ConstantesLogs.LOG_REMOTO_ACTIVO)) || (clave.equals(Q70ConstantesLogs.LOG_REMOTO_URL))){
					//Tratamiento para LOG_REMOTO
					if ((clave.equals(Q70ConstantesLogs.LOG_REMOTO_URL)) && (codAplicacion.equals(CODIGO_APLICACION_FRAMEWORK))) {
						valor = (String)atributos.get(clave);
						indiceClave = configuracionFinalLogRemoto.indexOf("\"" + clave + "\"");
						indiceValor = configuracionFinalLogRemoto.indexOf("\"", indiceClave+clave.length()+2);
						indiceFinValor = configuracionFinalLogRemoto.indexOf("\"", indiceValor+1);
						configuracionFinalLogRemoto = configuracionFinalLogRemoto.substring(0, indiceValor) + "\"" + valor + configuracionFinalLogRemoto.substring(indiceFinValor);
					}
				} else {
					//Tratamiento logger cocreto (incidencias, navegación, accesos, conectores, transacciones)
					valor = (String)atributos.get(clave);
					indiceClave = configuracionFinal.indexOf("\"" + clave + "\"");
					indiceValor = configuracionFinal.indexOf("\"", indiceClave+clave.length()+2);
					indiceFinValor = configuracionFinal.indexOf("\"", indiceValor+1);
					configuracionFinal = configuracionFinal.substring(0, indiceValor) + "\"" + valor + configuracionFinal.substring(indiceFinValor);
				}
			}
				
			configuracionFinalLogRemoto = configuracionInicialLogRemoto + configuracionFinalLogRemoto;
			indiceLogRemoto = configuracionFinalLogRemoto.indexOf(subComponente);
			configuracionFinalLogRemoto = configuracionFinalLogRemoto.substring(0 , indiceLogRemoto);
			
			configuracionFinal = configuracionFinalLogRemoto + configuracionFinal;
				
			resultado = persisteConfiguracionXML(listaInstancias, instanciaSeleccionada, tipo, codAplicacion, configuracionFinal);
		
		} catch (Throwable e) {
			Q70TrazaInterface traza = Q70Factoria.getTraza();
			if (traza.isTraceActive()){
				traza.trace(ID_COMPONENTE, Q70TraceLevel.ERROR, "Se ha producido un error en el método persisteConfiguracionXMLTrazaLogs");
			}
			throw new Q70FrameworkException(ID_COMPONENTE,Q70GestorArranqueConfiguracionServidor.class.getName(),"persisteConfiguracionXMLTrazaLogs","Se ha producido un error en el método persisteConfiguracionXMLTrazaLogs.",e);
		}
		return resultado;
	}
	
	/**
	 * Método que recupera el path de un fichero de configuración en base a un nombre de componente.
	 * @param nombreComponente
	 * @return
	 */
	private static String pathConfiguracionComponente (String nombreComponente)
	{
		int i = 0;
		for (i=0; i<nombreComponentesGlobales.size(); i++){
			if (((String)nombreComponentesGlobales.get(i)).equalsIgnoreCase(nombreComponente)){
				break;
			}
		}
		return (String) configuracionComponentesGlobales.get(i);
	}
	
	/**
	 * Arranca la infraestructura de JMX.
	 * <p>
	 * Además registra el primer bean de monitorización en el registry.
	 */
	private void prepararInfraestructuraJMX() throws Q70FrameworkException{
		try {
			Class claseMBeanServer = cargarClase(nombreGestorMBeanServer);
			gestorMBeanServer = (Q70GestorMBeanServer) claseMBeanServer.getMethod("getSingleton", null).invoke(null,null);
			gestorMBeanServer.setMapaPropiedades(atributosMBeanServer);
			mBeanServerDefault = gestorMBeanServer.getMBeanServer();
			
			instrumentarYRegistrar(new Q70BeanMonitorizacion(), CODIGO_APLICACION_FRAMEWORK,false);
				
		} 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 prepararInfraestructuraJMX");
			}
			throw new Q70FrameworkException(ID_COMPONENTE,this.getClass().toString(),"prepararInfraestructuraJMX","Se ha producido un error en el método prepararInfraestructuraJMX.",e);

		}		
	}
	private void instrumentarYRegistrar(Object objetoGestionable, String codAplicacion, boolean MBean) throws Exception {
		ObjectName miObjectName = null;
		if (MBean){
			((Q70MBeanInterface)objetoGestionable).setAplicacion(codAplicacion);
			miObjectName = ((Q70MBeanInterface)objetoGestionable).getNombreMBean();
		}
		else
			miObjectName = new ObjectName(getObjectName(objetoGestionable.getClass().getName(), codAplicacion));

		mBeanServerDefault.registerMBean(objetoGestionable, miObjectName);
		if (MBean && null!=instanciaListenerMBean)
			mBeanServerDefault.addNotificationListener(miObjectName, instanciaListenerMBean, null, null);
		
		Q70LogInterno.log("Se ha registrado el bean de monitorización "+miObjectName);		
	}
	
	private void beforeDestroyMBean(String tipo, String codAplicacion) throws Q70FrameworkException{
		Q70TrazaInterface traza = Q70Factoria.getTraza();
		try {
			invokeMBean(mBeanServerDefault, tipo, codAplicacion, "beforeDestroy", new Object[0], new String[0]);
			if (traza.isTraceActive()){
				traza.trace(COMPONENTE, Q70TraceLevel.DEBUG, "Destruyendo..." + Q70MBeanBase.getNombreMBean(tipo,codAplicacion));
			}
			unRegistrarMBean(tipo,codAplicacion);
		} catch (Throwable e) {
			if (traza.isTraceActive()){
				traza.trace(COMPONENTE, Q70TraceLevel.ERROR, "Se ha producido un error en el método beforeDestroyMBean");
			}
			throw new Q70FrameworkException(COMPONENTE,this.getClass().toString(),"beforeDestroyMBean","Se ha producido un error en el método beforeDestroyMBean.",e);
		}
	}
	
	private void reconfigureComponenteMBean(String strTipoComponente, String nombreClase, String pathFicheroXml, String nombreClaseMBean, String strNombreClasePersistencia, String codAplicacion) throws Q70FrameworkException{
		String path = pathFicheroXml;
		if (COMPONENTE_DE_APLICACION.equals(strTipoComponente)){
			path = getPathAplicacion(pathFicheroXml);
		}
		Q70TrazaInterface traza = Q70Factoria.getTraza();
		try {
			invokeMBean(mBeanServerDefault, nombreClaseMBean, codAplicacion, "reConfigure", new Object[]{path}, new String[]{"java.lang.String"});
		} catch (InstanceNotFoundException e) {
			try {
				if (COMPONENTE_DE_APLICACION.equals(strTipoComponente)){
					int numeroWarsAplicacion = classLoaderMap.getNumeroWarsAplicacion(codAplicacion);
					// para actualizar el contador de wars para la aplicacion
					for (int i = 0; i < numeroWarsAplicacion; i++) {
						doArrancarComponente(strTipoComponente, nombreClase, pathFicheroXml, nombreClaseMBean, strNombreClasePersistencia, codAplicacion);
					}
				}
				else{
					doArrancarComponente(strTipoComponente, nombreClase, pathFicheroXml, nombreClaseMBean, strNombreClasePersistencia, codAplicacion);
				}
			}catch (Throwable t){
				if (traza.isTraceActive()){
					traza.trace(ID_COMPONENTE, Q70TraceLevel.ERROR, "Se ha producido un error en el método reconfigureComponenteMBean");
				}
				throw new Q70FrameworkException(ID_COMPONENTE,this.getClass().toString(),"reconfigureComponenteMBean","Se ha producido un error en el método reconfigureComponenteMBean.",t);
			}
		} catch (Throwable e) {
			if (traza.isTraceActive()){
				traza.trace(ID_COMPONENTE, Q70TraceLevel.ERROR, "Se ha producido un error en el método reconfigureComponenteMBean");
			}
			throw new Q70FrameworkException(ID_COMPONENTE,this.getClass().toString(),"reconfigureComponenteMBean","Se ha producido un error en el método reconfigureComponenteMBean.",e);
		}
	}

	public static Set getBeansMonitorizacion(Q70Instancia instanciaSeleccionada) throws Q70FrameworkException {
		try {
			MBeanServer mBeanserver = getInstancia(instanciaSeleccionada);
			Set beans = mBeanserver.queryNames(new ObjectName(Q70MBeanBase.getDominioMBean()+":*,type=BeanMonitorizacionAplicacion"),null);
			return beans; 
		} 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 getBeansMonitorizacion");
			}
			throw new Q70FrameworkException(ID_COMPONENTE,Q70GestorArranqueConfiguracionServidor.class.getName(),"getBeansMonitorizacion","Se ha producido un error en el método getBeansMonitorizacion.",e);
		}
	}
	
	/**
	 * @see es.ejie.frmk.infraestructura.utilidades.configurador.Q70Configurable#beginConfiguration()
	 */
	public void beginConfiguration() {
	}

	/**
	 * @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) {
	}

	/**
	 * @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) {
		
		if (!"MBeanServer".equals(strNombre))
			nombreComponente = 	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)
		{
			if (tipoComponente.equalsIgnoreCase("Aplicacion"))
			{
				nombreComponentesAplicacion.add(nombreComponente);
				claseComponentesAplicacion.add(nombreClase);
				configuracionComponentesAplicacion.add(ficheroConfiguracion);
				claseMBeanAplicacion.add(nombreClaseMBean);
				clasePersistenciaAplicacion.add(nombreClasePersistencia);
			}
			else
			{
				if (!"MBeanServer".equals(strNombre)){
					nombreComponentesGlobales.add(nombreComponente);
					claseComponentesGlobales.add(nombreClase);
					configuracionComponentesGlobales.add(ficheroConfiguracion);
					claseMBeanGlobal.add(nombreClaseMBean);
					clasePersistenciaGlobal.add(nombreClasePersistencia);
				}
			}
			nombreComponente = null;
			nombreClase = null;
			ficheroConfiguracion = null;
			nombreClaseMBean = null;
			nombreClasePersistencia = null;
			
		}

	/**
	 * @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) {

		if (strNombre.equalsIgnoreCase("NombreClase"))
		{
			nombreClase = strValor;
		} 
		else if (strNombre.equalsIgnoreCase("FicheroConfiguracion"))
		{
			ficheroConfiguracion = strValor;
		}
		else if (strNombre.equalsIgnoreCase("NombreClaseMBean"))
		{
			nombreClaseMBean = strValor;
		}
		else if (strNombre.equalsIgnoreCase("NombreClasePersistencia"))
		{
			nombreClasePersistencia = strValor;
		}
		else if (strNombre.equalsIgnoreCase("Log_Interno_Activo")) {
			if (strValor.equalsIgnoreCase("SI") && esDespliegueGlobal)
				Q70LogInterno.setEnabled(true);
			else
				Q70LogInterno.setEnabled(false);
		}else if (strNombre.equalsIgnoreCase("nombreGestorMBeanServer")) {
			nombreGestorMBeanServer = strValor;
		}else if (strNombre.equalsIgnoreCase("nombreClaseListenerMBean")) {
			try {
				Class listenerMBean = cargarClase(strValor);
				instanciaListenerMBean = (NotificationListener)listenerMBean.newInstance();
			} catch (Exception e) {
				Q70LogInterno.log("No se ha podido instanciar la clase del listener: "+strValor +", produciendose la excepcion : "+ e.getMessage());
			}
		}else{//propiedades del MBeanServer
			atributosMBeanServer.put(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) {
	}

	/**
	 * Para leer una propiedad (key) del fichero de properties de configuración de las propiedades (propertiesPath).
	 * <p> 
	 * Método que lee una propiedad de la aplicación
	 * de forma optimizada gracias a la caché
	 * 
	 * @param componenteTraza Componente que genera la traza
	 * @param propertiesPath Path del fichero de properties
	 * @param key Nombre de la propiedad buscada
	 * 
	 * @return Valor de la propiedad buscada
	 */
	public static String getApplicationProperty (String componenteTraza, String propertiesPath, String key) {
		return getApplicationProperty(componenteTraza,propertiesPath,key,true);
	}
	
	/**
	 * Para leer una propiedad (key) del fichero de properties de configuración de las propiedades (propertiesPath).
	 * <p> 
	 * 
	 * @param componenteTraza Componente que genera la traza
	 * @param propertiesPath Path del fichero de properties
	 * @param key Nombre de la propiedad buscada
	 * @param usarCacheDeAplicacion True, si se usa Cache de aplicacion, para la busqueda. False, en caso contrario.
	 *  
	 * @return Valor de la propiedad buscada
	 */
	public static String getApplicationProperty (String componenteTraza, String propertiesPath, String key, boolean usarCacheDeAplicacion) {
		String codigoAP = null;
		if (propertiesPath==null) {
			try {
				codigoAP = classLoaderMap.getServletContext().getInitParameter(NOMBRE_APLICACION);
			} catch (Q70FrameworkException e) {
				Q70GestorDeExcepcionesServidor.getSingleton().recogerThrowable(e);
			}
		}
		return getProperty(componenteTraza,propertiesPath,key,codigoAP,usarCacheDeAplicacion);
	}
	
	/**
	 * Para leer una propiedad (key) del fichero de properties de configuración del Framework.
	 * 
	 * Lee una propiedad del framework
	 * de forma optimizada gracias a la caché
	 * 
	 * @param key Nombre de la propiedad buscada
	 * 
	 * @return Valor de la propiedad buscada
	 */
	public static String getGlobalProperty (String key) {
		return getProperty(ID_COMPONENTE,null,key,CODIGO_APLICACION_FRAMEWORK,false);
	}

	/**
	 * Para leer una propiedad (key) del fichero de properties de configuración de las propiedades (propertiesPath)
	 * de una aplicacion dada.
	 * <p> 
	 * Método que lee una propiedad de la aplicación
	 * de forma optimizada gracias a la caché
	 * 
	 * @param propertiesPath Path del fichero de properties
	 * @param key Nombre de la propiedad buscada
	 * @param codigoAP Codigo de la aplicacion que genera la traza
	 * 
	 * @return Valor de la propiedad buscada
	 */
	public static String getProperty(String propertiesPath, String key, String codigoAP) {
		return getProperty(ID_COMPONENTE, propertiesPath, key, codigoAP, true);
	}

	/**
	 * Para leer una propiedad (key) del fichero de properties de configuración de las propiedades (propertiesPath)
	 * de una aplicacion dada.
	 * <p> 
	 * @param propertiesPath Path del fichero de properties
	 * @param key Nombre de la propiedad buscada
	 * @param codigoAP Codigo de la aplicacion que genera la traza
	 * @param usarCacheDeAplicacion True, si se usa Cache de aplicacion, para la busqueda. False, en caso contrario.
	 * 
	 * @return Valor de la propiedad buscada
	 */
	public static String getProperty(String propertiesPath, String key, String codigoAP, boolean usarCacheDeAplicacion) {
		return getProperty(ID_COMPONENTE, propertiesPath, key, codigoAP, usarCacheDeAplicacion);
	}
	
	/**
	 * Método para la lectura de un valor de una entrada de un fichero de propiedades en base a un key.
	 * 
	 * @param componenteTraza Id del componente para sacar la traza
	 * @param propertiesPath Path en el que se ubica el fichero de propiedades
	 * @param key Clave para recuperar la entrada del fichero de propiedades
	 * @param codigoAP Código de la aplicación
	 * 
	 * @return Valor de la propiedad buscada
	 */
	private static String getProperty(String componenteTraza, String propertiesPath, String key, String codigoAP, boolean usarCacheDeAplicacion) {
		if (propertiesPath == null) {
			// Si buscamos propiedad de q70 y se ha dado Q70ENTORNO
			propertiesPath = doGetPathProperties(codigoAP);
		}
		Properties propiedades = loadProperties(componenteTraza, propertiesPath, null, usarCacheDeAplicacion);

		// Devolvemos el resultado
		String resultado = propiedades.getProperty(key);
		return resultado;
	}

	private static String doGetPathProperties(String codigoAP) {
		String propertiesPath;
		if (codigoAP.equals(CODIGO_APLICACION_FRAMEWORK) && existeParametroq70entorno) {
			propertiesPath = "/" + PATH_CONFIGURACION_GLOBAL + codigoAP + ".properties";
		} else {
			propertiesPath = "/" + codigoAP + doGetPathConfigAplicacion(codigoAP) + codigoAP + ".properties";
		}
		return propertiesPath;
	}

	public static Properties loadProperties(String componenteTraza, String propertiesPath, ClassLoader classloader) {
		return loadProperties(componenteTraza, propertiesPath, classloader, true);
	}
	
	public static Properties loadProperties(String componenteTraza, String propertiesPath, ClassLoader classloader, boolean usarCacheDeAplicacion) {
		Properties propiedades = null;
		if (propertiesPath != null) { 
			if (propertiesPath.equals(doGetPathProperties(CODIGO_APLICACION_FRAMEWORK))) {
				if (null!=Q70GestorCacheServidor.getSingleton()) {
					propiedades = (Properties) Q70GestorCacheServidor.getSingleton().getElement(Q70Constantes.CACHE_Q70_PROPERTIES,propertiesPath);
				}
			} else {
				if (usarCacheDeAplicacion){
					if (null!=(Q70GestorCacheServidorInterface)Q70Factoria.getGestorCache()) {
						propiedades = (Properties) ((Q70GestorCacheServidorInterface)Q70Factoria.getGestorCache()).getElement(Q70Constantes.CACHE_PROPERTIES,propertiesPath);
					}
				}
			} 
		}
		if (null == propiedades) {
			// No se ha encontrado el fichero de propiedades en la cache
			// Busco el fichero de propiedades
			propiedades = loadPropertiesFromFile(componenteTraza, propertiesPath, classloader);
			// Una vez cargado lo guardamos en cache
			if (propertiesPath != null) { 
				if (propertiesPath.equals(doGetPathProperties(CODIGO_APLICACION_FRAMEWORK))) {
					if (null!=Q70GestorCacheServidor.getSingleton()) {
						Q70GestorCacheServidor.getSingleton().addElement(Q70Constantes.CACHE_Q70_PROPERTIES, propertiesPath, propiedades);
					}
				} else {
					if (usarCacheDeAplicacion){
						if (null!=(Q70GestorCacheServidorInterface)Q70Factoria.getGestorCache()) {
							((Q70GestorCacheServidorInterface)Q70Factoria.getGestorCache()).addElement(Q70Constantes.CACHE_PROPERTIES, propertiesPath, propiedades);
						}
					}
				} 
			}
		}		
		return propiedades;
	}

	public static Properties loadPropertiesFromFile(String componenteTraza, String propertiesPath, ClassLoader classloader) {
		Properties propiedades = null;
		try {
			propiedades = new Properties();
			
			InputStream resourceAsStream = Q70GestorArranqueConfiguracionServidor.class.getResourceAsStream(propertiesPath);
			if (classloader!=null){
				resourceAsStream = classloader.getResourceAsStream(propertiesPath);
			}
			propiedades.load(resourceAsStream);
		} catch (Exception e) {
			Q70TrazaInterface traza = Q70Factoria.getTraza();
			if (traza.isTraceActive()){
				traza.trace(componenteTraza, Q70TraceLevel.ERROR, "Error recuperando el fichero de propiedades "+propertiesPath);
				return null;
			}
		}
		return propiedades;
	}

	/**
	 * Lee un recurso en /WEB-INF/aplic/aaa/exe.
	 * @param resourceName
	 * @return
	 */
	public static InputStream getApplicationResourceAsStream(String resourceName) {
		ServletContext servletContext = null;
		try {
			servletContext = classLoaderMap.getServletContext();
		} catch (Q70FrameworkException e) {
			Q70GestorDeExcepcionesServidor.getSingleton().recogerThrowable(e);
		}
		
		if (servletContext==null){
			return null;
		}
		
		String codigoAP = servletContext.getInitParameter(NOMBRE_APLICACION);
		InputStream is = servletContext.getResourceAsStream(PREFIJO_APLICACION_DENTRO_WAR+codigoAP+SUFIJO_APLICACION+resourceName);
		return is;
	}
	
	/**
	 * Llamada a la lectura de un recurso en /aplic/html/datos pasandole el classloader y el nombre del recurso
	 * @param clase
	 * @param resourceName
	 * @return InputStream
	 */
	public static InputStream getApplicationDataAsStream(Class clase, String resourceName) {
		return getApplicationDataAsStream0(clase.getClassLoader(), resourceName);
	}
	
	/**
	 * Llamada a la lectura de un recurso en /aplic/html/datos pasandole solamente el nombre del recurso.
	 * El classloader para leer el recurso sera el de por defecto, el del thread.
	 * @param resourceName
	 * @return
	 */
	public static InputStream getApplicationDataAsStream(String resourceName) {
		return getApplicationDataAsStream0(Thread.currentThread().getContextClassLoader(), resourceName);
	}
	        
	/**
	 * Lee un recurso en /aplic/aaa/html/datos.
	 * Este recurso debe leerse por classloader y por tanto el path estar en el classpath del sistema
	 * @param resourceName
	 * @param contextClassLoader 
	 * @return
	 */
	private static InputStream getApplicationDataAsStream0(ClassLoader contextClassLoader, String resourceName) {
		//En el classpath debe estar aplic/aaa
		String pathCompleto = "html/datos/"+resourceName;
		Q70LogInterno.log(pathCompleto);
		InputStream is = contextClassLoader.getResourceAsStream(pathCompleto);
		return is;
	}	
	/**
	 * Lee un recurso en /config/aaa/.
	 * Este recurso debe leerse por classloader y por tanto el path (sin aaa) estar en el classpath del sistema
	 * @param resourceName
	 * @return
	 */
	public static InputStream getApplicationConfigAsStream(String resourceName) {
		String pathCompleto = getPathAplicacion(resourceName);
		InputStream is = ClassLoader.getSystemResourceAsStream(pathCompleto);
		return is;
	}

	public static String getPathAplicacion(String resourceName) {
		String codigoAP = "";
		try {
			codigoAP = classLoaderMap.getServletContext().getInitParameter(NOMBRE_APLICACION);
		} catch (Q70FrameworkException e) {
			Q70GestorDeExcepcionesServidor.getSingleton().recogerThrowable(e);
		}
		if (resourceName.startsWith("/"))
			resourceName = StringUtils.substringAfter(resourceName,"/");
		
		return codigoAP + doGetPathConfigAplicacion(codigoAP) + resourceName;
	}
	
	/**
	 * Método encargado de la lectura de un recurso de aplicación ubicado en /config/xxx/
	 * @param resourceName
	 * @param codigoAplicacion
	 * @return
	 */
	private static InputStream getApplicationConfigFile (String resourceName, String codigoAplicacion) {
		if (resourceName.startsWith("/"))
			resourceName = StringUtils.substringAfter(resourceName,"/");
		
		String pathCompleto = codigoAplicacion + doGetPathConfigAplicacion(codigoAplicacion) + resourceName;
		InputStream is = ClassLoader.getSystemResourceAsStream(pathCompleto);
		return is;
	}

	/**
	 * Notificacion de que una sesion ha sido creada
	 * No se realiza ninguna accion
	 * @param resourceName 
	 */
	public void sessionCreated(HttpSessionEvent se) {
	}

	/**
	 * Notificacion de que una sesion ha sido invalidada
	 * @param resourceName
	 */
	public void sessionDestroyed(HttpSessionEvent se) {	
	}

	/**
	 * Método para realizar la persistencia de la configuración de un componente de una aplicación determinada.
	 * @param listaInstancias lista de instancias disponibles
	 * @param instanciaSeleccionada - instancia a la que pertenece el componente
	 * @param tipo - tipo de componente
	 * @param codAplicacion - codigo de aplicacion
	 * @param strFicheroXml - contenido del fichero xml
	 * @return
	 * @throws Q70FrameworkException
	 */
	public static Object persisteConfiguracionXML(List listaInstancias, Q70Instancia instanciaSeleccionada, String tipo, String codAplicacion, String strFicheroXml) throws Q70FrameworkException{
		return doPersisteConfiguracionXML(listaInstancias, instanciaSeleccionada, tipo, codAplicacion, strFicheroXml, null);
	}
	
	
	/**
	 * Método para realizar la persistencia de la configuración de un componente.
	 * @param listaInstancias - lista de instancias disponibles
	 * @param instanciaSeleccionada - instancia a la que pertenece el componente a reconfigurar
	 * @param nombreComponente - nombre del componente
	 * @param strFicheroXml - contenido del fichero xml
	 * @return
	 * @throws Q70FrameworkException
	 */
	public static Object persisteConfiguracionXML(List listaInstancias, Q70Instancia instanciaSeleccionada, String nombreComponente, String strFicheroXml) throws Q70FrameworkException{
		return doPersisteConfiguracionXML(listaInstancias, instanciaSeleccionada, Q70BeanMonitorizacion.class.getName(), CODIGO_APLICACION_FRAMEWORK, strFicheroXml, nombreComponente);
	}
	
	public static Object persisteFicheroPropiedades(List listaInstancias, Q70Instancia instanciaSeleccionada, String codAplicacion, String strFicheroXml) throws Q70FrameworkException{
		return doPersisteConfiguracionXML(listaInstancias, instanciaSeleccionada, Q70BeanMonitorizacion.class.getName(), codAplicacion, strFicheroXml, null);
	}

	/**
	 * Método encargado de la invocación a la persistencia de un componente de configuración. Realiza un tratamiento 
	 * específico en el caso de que el componente pertenezca a una instancia de Cluster, ya que en tal caso habrá 
	 * que realizar dicha persistencia sobre ese componente en cada una de las instancias del Cluster.
	 * @param listaInstancias lista de instancias de Geremua disponibles
	 * @param instanciaSeleccionada instancia de Geremua a la que pertenece el componente sobre el que va a realizarse
	 *        la persistencia
	 * @param tipo MBean del componente que va a pesistirse (logs, trazas, caché,...)
	 * @param codAplicacion código de aplicación al que pertenece el componente que va a reconfigurarse.
	 * @param strFicheroXml contenido del fichero xml con el que va a ser actualizado el componente
	 * @param nombreComponente nombre del componente
	 * @return
	 * @throws Q70FrameworkException
	 */
	private static Object doPersisteConfiguracionXML(List listaInstancias, Q70Instancia instanciaSeleccionada, String tipo, String codAplicacion, String strFicheroXml, String nombreComponente) throws Q70FrameworkException{
		Object resultado = null;
		try {
			Q70Instancia instancia = null;
					
			ArrayList listaExcepciones = new ArrayList(5);
			if ((instanciaSeleccionada.getNombreCluster()!= null) && (!instanciaSeleccionada.getNombreCluster().equals(""))) {
				List listaInstanciasCluster = getInstanciasCluster(listaInstancias, instanciaSeleccionada.getNombreCluster());

				for (int i=0; i< listaInstanciasCluster.size(); i++){
					instancia = (Q70Instancia)listaInstanciasCluster.get(i);
					try {
						listaExcepciones= persisteInstancia(listaExcepciones, instancia, tipo, codAplicacion, strFicheroXml, nombreComponente);
					} 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 doPersisteConfiguracionXML, en el cluster");
						}
					}
				}
			} else {
				listaExcepciones = persisteInstancia(listaExcepciones, instanciaSeleccionada, tipo, codAplicacion, strFicheroXml, nombreComponente);
			}
			resultado = listaExcepciones;
		} 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 doPersisteConfiguracionXML");
			}
			throw new Q70FrameworkException(ID_COMPONENTE,Q70GestorArranqueConfiguracionServidor.class.getName(),"doPersisteConfiguracionXML","Se ha producido un error en el método doPersisteConfiguracionXML.",e);
		}
		return resultado;
	}
	
	/**
	 * Método que realiza la invocación JMX para la reconfiguración y persistencia de componentes de configuración 
	 * de una instancia de Geremua.
	 * @param listaExcepciones Lista de los errores generados con anterioridad a la llamada de este metodo.
	 * @param instancia de Geremua a la que pertenece el componente que se va a reconfigurar.
	 * @param tipo de componente (logs, trazas, caché, tablas códigos, mail,...)
	 * @param codAplicacion del fichero que va a reconfigurarse
	 * @param strFicheroXml contenido final de fichero de configuración que se va a persistir.
	 * @param nombreComponente
	 * @return Lista de los errores
	 */
	private static ArrayList persisteInstancia (ArrayList listaExcepciones, Q70Instancia instancia, String tipo, String codAplicacion, String strFicheroXml, String nombreComponente){
		ArrayList listaExcepcionesAux = listaExcepciones;
		try {
			MBeanServer mBeanserver = getInstancia(instancia);
			if (tipo.equals(Q70BeanMonitorizacion.class.getName())){
				if (nombreComponente==null){
					invocarAccionJMX(mBeanserver,"reconfigurarFicheroPropiedades", new Object[]{codAplicacion, strFicheroXml}, new String[]{"java.lang.String", "java.lang.String"}, CODIGO_APLICACION_FRAMEWORK);
				}else{
					invocarAccionJMX(mBeanserver,"reconfigurarComponente", new Object[]{nombreComponente, strFicheroXml}, new String[]{"java.lang.String", "java.lang.String"}, codAplicacion);
				}
			}
			else{
				invokeMBean(mBeanserver, tipo, codAplicacion, "persisteConfiguracionXML", new Object[]{strFicheroXml}, new String[]{"java.lang.String"});
			}
		} catch (Exception e){
			String strMensaje = "La instancia '" + instancia.getNombre() + "' ha producido un error ";
			if (e instanceof MBeanException) {
				Throwable excAnidada = ((MBeanException)e).getTargetException();
				if (excAnidada != null) {
					if (excAnidada instanceof Q70FrameworkException) {
						strMensaje += "'" + ((Q70FrameworkException) excAnidada).getPrimaryException().getMessage() + "'";
					} else {
						strMensaje += "'" + excAnidada.getMessage() + "'";
					}
				}
			}
			Q70FrameworkException exception = new Q70FrameworkException(ID_COMPONENTE,"Q70GestorArranqueConfiguracionServidor","persisteInstancia",strMensaje,e);
			listaExcepcionesAux.add(exception);
			Q70GestorDeExcepcionesServidor.getSingleton().recogerThrowable(exception);
		}
		return listaExcepcionesAux;
	}

	/**
	 * Método que realiza la invocación para la recuperación de la configuración de un componente de una instancia
	 * de Geremua.
	 * @param mBeanServer
	 * @param tipo del componente del que vamos a recuperar su configuración (trazas, logs, caché,...) 
	 * @param codAplicacion al que pertenece el componente
	 * @return
	 * @throws Q70FrameworkException
	 */
	public static String recuperaConfiguracionXML(MBeanServer mBeanServer, String tipo, String codAplicacion) throws Q70FrameworkException{
		try {
			return (String)invokeMBean(mBeanServer, tipo, codAplicacion, "recuperaConfiguracionXML", new Object[0], new String[0]);
		} 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 recuperaConfiguracionXML");
			}
			throw new Q70FrameworkException(ID_COMPONENTE,Q70GestorArranqueConfiguracionServidor.class.getName(),"recuperaConfiguracionXML","Se ha producido un error en el método recuperaConfiguracionXML.",e);
		}
	}

	private void unRegistrarComponente(ObjectName oName) throws Exception {
		mBeanServerDefault.unregisterMBean(oName);
	}

	private static String getObjectName(String beanMonitorizacion, String codAplicacion){
		String tipo = null;
		if (CODIGO_APLICACION_FRAMEWORK.equals(codAplicacion))
			tipo = "BeanMonitorizacion";
		else
			tipo = "BeanMonitorizacionAplicacion";
		return Q70MBeanBase.getDominioMBean()+":"+Q70MBeanBase.CLASS+"="+beanMonitorizacion+","+Q70MBeanBase.TYPE+"="+tipo+","+Q70MBeanBase.APPLICATION+"="+codAplicacion;
	}

	private void doGrabarInstanciaLevantada() throws Q70FrameworkException{
		PreparedStatement pstmt = null;
		Connection conn = null;
		
		try {
			conn = Q70ConectorJDBC.getSingleton().getConnection(getGlobalProperty(Q70_DATASOURCE));

			String sql = "INSERT INTO "+getGlobalProperty(Q70_TABLA_INSTANCIA)+" (CPD, URL_INSTANCIA, ID_INSTANCIA, NBR_CLUSTER, ACTIVADO) VALUES ('"
					+ getGlobalProperty(Q70_CPD_INSTANCIA)
					+ "','"
					+ gestorMBeanServer.getUrlInstancia()
					+ "','"
					+ gestorMBeanServer.getIdInstancia()
					+ "','"
					+ gestorMBeanServer.getNombreClusterInstancia() + "','S') ";
			pstmt = conn.prepareStatement(sql);
			pstmt.executeUpdate();

		} catch (SQLException sql) {
			// Si la excepcion es porque hay un duplicado en la tabla, entonces modificar registro y tracear.
			if (sql.getSQLState().equals("23000")){
				Q70TrazaInterface traza = Q70Factoria.getTraza();
				if (traza.isTraceActive()){
					traza.trace(ID_COMPONENTE,Q70TraceLevel.INFO,"Existe un registro para la instancia '"+ gestorMBeanServer.getUrlInstancia() +"' en la Tabla, se procede a su modificación con los datos actuales. Posiblemente, el servidor se terminó mediante un proceso kill.");
				}	
				doModificarInstanciaLevantada();
			}else{
				Q70TrazaInterface traza = Q70Factoria.getTraza();
				if (traza.isTraceActive()){
					traza.trace(ID_COMPONENTE,Q70TraceLevel.ERROR,"Excepción en inserción de una Instancia administrable:'"+sql.getMessage()+"','"+sql.getClass().getName()+"'");
				}	
				throw new Q70FrameworkException(ID_COMPONENTE,Q70GestorArranqueConfiguracionServidor.class.getName(),"doGrabarInstanciaLevantada","Se ha producido un error en el método doGrabarInstanciaLevantada.",sql);
			}
		} catch (Exception e) {
			Q70TrazaInterface traza = Q70Factoria.getTraza();
			if (traza.isTraceActive()){
				traza.trace(ID_COMPONENTE,Q70TraceLevel.ERROR,"Excepción en inserción de una Instancia administrable:'"+e.getMessage()+"','"+e.getClass().getName()+"'");
			}	
			throw new Q70FrameworkException(ID_COMPONENTE,Q70GestorArranqueConfiguracionServidor.class.getName(),"doGrabarInstanciaLevantada","Se ha producido un error en el método doGrabarInstanciaLevantada.",e);
        } finally {
			try {
				if (pstmt!=null) pstmt.close();				
			} catch (Exception e) {}
			try {
				if (conn!=null) conn.close();
			} catch (Exception e) {}			
        }
	}

	private void doModificarInstanciaLevantada() throws Q70FrameworkException{
		PreparedStatement pstmt = null;
		Connection conn = null;
		
		try {
			conn = Q70ConectorJDBC.getSingleton().getConnection(getGlobalProperty(Q70_DATASOURCE));

			String sql = "UPDATE "+getGlobalProperty(Q70_TABLA_INSTANCIA)+" SET ID_INSTANCIA='" + gestorMBeanServer.getIdInstancia()
					+ "', ACTIVADO='S' WHERE CPD='" + getGlobalProperty(Q70_CPD_INSTANCIA)
					+ "' AND URL_INSTANCIA='" + gestorMBeanServer.getUrlInstancia() + "'"; 
			pstmt = conn.prepareStatement(sql);
			pstmt.executeUpdate();

		} catch (Exception e) {
			Q70TrazaInterface traza = Q70Factoria.getTraza();
			if (traza.isTraceActive()){
				traza.trace(ID_COMPONENTE,Q70TraceLevel.ERROR,"Excepción al modificar una Instancia administrable:'"+e.getMessage()+"','"+e.getClass().getName()+"'");
			}	
			throw new Q70FrameworkException(ID_COMPONENTE,Q70GestorArranqueConfiguracionServidor.class.getName(),"doModificarInstanciaLevantada","Se ha producido un error en el método doModificarInstanciaLevantada.",e);
        } finally {
			try {
				if (pstmt!=null) pstmt.close();				
			} catch (Exception e) {}
			try {
				if (conn!=null) conn.close();
			} catch (Exception e) {}			
        }
	}
	
	private void doBorrarInstanciaLevantada() throws Q70FrameworkException{
		PreparedStatement pstmt = null;
		Connection conn = null;
		
		try {
			conn = Q70ConectorJDBC.getSingleton().getConnection(getGlobalProperty(Q70_DATASOURCE));

			String sql = "DELETE FROM "+getGlobalProperty(Q70_TABLA_INSTANCIA)+" WHERE CPD='" + getGlobalProperty(Q70_CPD_INSTANCIA)
					+ "' AND URL_INSTANCIA='" + gestorMBeanServer.getUrlInstancia() + "'";
			pstmt = conn.prepareStatement(sql);
			pstmt.executeUpdate();
			
		} catch (Exception e) {
			Q70TrazaInterface traza = Q70Factoria.getTraza();
			if (traza.isTraceActive()){
				traza.trace(ID_COMPONENTE,Q70TraceLevel.ERROR,"Excepción en borrado de una Instancia administrable:'"+e.getMessage()+"','"+e.getClass().getName()+"'");
			}			
			throw new Q70FrameworkException(ID_COMPONENTE,Q70GestorArranqueConfiguracionServidor.class.getName(),"doBorrarInstanciaLevantada","Se ha producido un error en el método doBorrarInstanciaLevantada.",e);
        } finally {
			try {
				if (pstmt!=null) pstmt.close();				
			} catch (Exception e) {}
			try {
				if (conn!=null) conn.close();
			} catch (Exception e) {}			
        }
	}

	/**
	 * Método que monta y ejecuta la query para recuperar de BBDD las instancias disponibles y retorna una lista
	 * con dichas instancias
	 * @return listaComponentes lista con elementos de tipo Q70Instancia
	 * @throws Q70FrameworkException
	 */
	public static List getInstancias() throws Q70FrameworkException{
		ArrayList listaComponentes = new ArrayList(11);
        ResultSet rs = null;
		PreparedStatement pstmt = null;
		Connection conn = null;
		
		try {
			conn = Q70ConectorJDBC.getSingleton().getConnection(getGlobalProperty(Q70_DATASOURCE));
			String strCPD = getGlobalProperty(Q70_CPD_INSTANCIA);
			String sql = "SELECT CPD, URL_INSTANCIA, ID_INSTANCIA, NBR_CLUSTER, ACTIVADO FROM "+getGlobalProperty(Q70_TABLA_INSTANCIA)+" WHERE CPD='" + strCPD +"' ORDER BY NBR_CLUSTER, URL_INSTANCIA";
			pstmt = conn.prepareStatement(sql);
			rs = pstmt.executeQuery();
			while (rs.next()){
				Q70Instancia instancia = new Q70Instancia();
			    instancia.setCpd(rs.getString("CPD"));
			    instancia.setUrl(rs.getString("URL_INSTANCIA"));
			    instancia.setIdInstancia(rs.getString("ID_INSTANCIA"));
			    instancia.setNombreCluster(rs.getString("NBR_CLUSTER"));
			    instancia.setActivado(rs.getString("ACTIVADO"));
			    listaComponentes.add(instancia);
			}
		} catch (Exception e) {
			Q70TrazaInterface traza = Q70Factoria.getTraza();
			if (traza.isTraceActive()){
				traza.trace(ID_COMPONENTE,Q70TraceLevel.ERROR,"Excepción en búsqueda de Instancias administrables:'"+e.getMessage()+"','"+e.getClass().getName()+"'");
			}
			throw new Q70FrameworkException(ID_COMPONENTE,Q70GestorArranqueConfiguracionServidor.class.getName(),"getInstancias","Se ha producido un error en el método getInstancias.",e);
        } finally {
			try {
				if (rs!=null) rs.close();
				if (pstmt!=null) pstmt.close();				
			} catch (Exception e) {}
			try {
				if (conn!=null) conn.close();
			} catch (Exception e) {}			
        }
		return listaComponentes;
	}
	
	/**
	 * Método que retorna una instancia de la clase MBeanServer en base a una dirección url de
	 * una instancia de Geremua
	 * @param instancia instancia de la clase Q70Instancia (de la que se extrae la url)
	 * @return
	 * @throws NamingException
	 */
	public static MBeanServer getInstancia(Q70Instancia instancia) throws NamingException{
		return gestorMBeanServer.getMBeanServer(instancia.getUrl());
	}
	
	/**
	 * Invocar una acción JMX sobre un componente.
	 * <p>
	 * Aisla al programador de la complejidad técnica del JMX. 
	 * Los argumentos son el nombre de la acción, el array de parámetros y el array de las clases de los parámetros
	 */	
	public static Object invocarAccionJMX(String nombreAccion, Object[] parametros, String signatura[], String codAplicacion)  throws Q70FrameworkException
	{
		return invocarAccionJMX(getObjectName(Q70BeanMonitorizacion.class.getName(), codAplicacion),nombreAccion,parametros,signatura);
	}

	/**
	 * Método para realizar una invocación jmx con una acción concreta a realizar sobre una instancia.
	 * @param mBeanServer
	 * @param nombreAccion
	 * @param parametros
	 * @param signatura
	 * @param codAplicacion
	 * @return
	 * @throws NamingException
	 * @throws Q70FrameworkException
	 */
	public static Object invocarAccionJMX(MBeanServer mBeanServer, String nombreAccion, Object[] parametros, String signatura[], String codAplicacion) throws NamingException, Q70FrameworkException
	{
		return invocarAccionJMX(mBeanServer,getObjectName(Q70BeanMonitorizacion.class.getName(), codAplicacion),nombreAccion,parametros,signatura);
	}

	/**
	 * Invocar una acción JMX sobre un componente.
	 * @param miOname
	 * @param nombreAccion
	 * @param parametros
	 * @param signatura
	 * @return
	 * @throws Q70FrameworkException
	 */
	public static Object invocarAccionJMX(String miOname, String nombreAccion, Object[] parametros, String signatura[]) throws Q70FrameworkException
	{
		return invocarAccionJMX(mBeanServerDefault,miOname,nombreAccion,parametros,signatura);
	}
	
	/**
	 * Invocar una acción JMX sobre un componente.
	 * <p>
	 * Aisla al programador de la complejidad técnica del JMX. 
	 * Los argumentos son el dominio, el nombre de la acción, el array de parámetros y el array de las clases de los parámetros
	 */	
	public static Object invocarAccionJMX(MBeanServer mBeanServer, String miOname, String nombreAccion, Object[] parametros, String signatura[]) throws Q70FrameworkException
	{
		try {			
			return mBeanServer.invoke(new ObjectName(miOname), nombreAccion, parametros, signatura);
		} catch (Exception e) {
			throw new Q70FrameworkException(ID_COMPONENTE,Q70GestorArranqueConfiguracionServidor.class.getName(),"invocarAccionJMX","Se ha producido un error en la invocación jmx.",e);
		}	
	}	

	/**
	 * Método que recupera una lista de MBeans para una instancia y tipo de componente concretos.
	 * @param beanServer
	 * @param tipoComponente
	 * @return
	 * @throws MalformedObjectNameException
	 */
	public static Collection queryListaMBeans(MBeanServer beanServer, String tipoComponente) throws MalformedObjectNameException {
		return beanServer.queryNames(Q70MBeanBase.getNombreMBean(tipoComponente),null);
	}
	
	/**
	 * Método que retorna un MBean concreto de una instancia en base a un código de aplicación y tipo de 
	 * componente.
	 * @param beanServer
	 * @param tipoComponente
	 * @param aplicacion
	 * @return
	 * @throws MalformedObjectNameException
	 * @throws Q70FrameworkException
	 */
	public static ObjectName queryMBean(MBeanServer beanServer, String tipoComponente, String aplicacion) throws MalformedObjectNameException, Q70FrameworkException {
		Set listaNombres = beanServer.queryNames(Q70MBeanBase.getNombreMBeanAplicacion(tipoComponente, aplicacion),null);	
		if (listaNombres.isEmpty()){
			throw new Q70FrameworkException(ID_COMPONENTE, Q70GestorArranqueConfiguracionServidor.class.getName(), "queryMBean", "No existe el MBean requerido:'"+tipoComponente+"','"+aplicacion+"' para la instancia:'"+beanServer+"'", null);
		}else{
			if (listaNombres.size()>1){
				throw new Q70FrameworkException(ID_COMPONENTE, Q70GestorArranqueConfiguracionServidor.class.getName(), "queryMBean", "Existe más de un MBean:'"+tipoComponente+"','"+aplicacion+"' para la instancia:'"+beanServer+"'", null);
			}
		}
		return (ObjectName)listaNombres.iterator().next();
	}	

	// solo se debe usar desde testCases
	public static Object invokeMBean(String nombreClaseMBean, String codAplicacion, String metodo, Object[] valoresParametros, String[] tipoParametros) throws MalformedObjectNameException, InstanceNotFoundException, InstanceAlreadyExistsException, MBeanException, ReflectionException {
		return invokeMBean(mBeanServerDefault,nombreClaseMBean,codAplicacion,metodo,valoresParametros,tipoParametros);
	}

	/**
	 * Método para la recuperación de un conjunto de MBeans sobre una instancia concreta.
	 * @param beanServer
	 * @param nombreClaseMBean
	 * @param codAplicacion
	 * @param metodo
	 * @param valoresParametros
	 * @param tipoParametros
	 * @return
	 * @throws MalformedObjectNameException
	 * @throws InstanceNotFoundException
	 * @throws InstanceAlreadyExistsException
	 * @throws MBeanException
	 * @throws ReflectionException
	 */
	public static Object invokeMBean(MBeanServer beanServer, String nombreClaseMBean, String codAplicacion, String metodo, Object[] valoresParametros, String[] tipoParametros) throws MalformedObjectNameException, InstanceNotFoundException, InstanceAlreadyExistsException, MBeanException, ReflectionException {
		Set listaNombres = beanServer.queryNames(Q70MBeanBase.getNombreMBean(nombreClaseMBean,codAplicacion),null);
		if (listaNombres.isEmpty()){
			throw new InstanceNotFoundException(Q70MBeanBase.getNombreMBean(nombreClaseMBean,codAplicacion).toString());
		}else{
			if (listaNombres.size()>1){
				throw new InstanceAlreadyExistsException(Q70MBeanBase.getNombreMBean(nombreClaseMBean,codAplicacion).toString());
			}else{
				return beanServer.invoke((ObjectName)listaNombres.iterator().next(), metodo, valoresParametros, tipoParametros);
			}
		}
	}

	/**
	 * Invocación para recuperar los MBeans de una instancia y aplicación determinada.
	 * @param tipoComponente
	 * @param codAplicacion
	 * @param metodo
	 * @param valoresParametros
	 * @param tipoParametros
	 * @return
	 * @throws MalformedObjectNameException
	 * @throws InstanceNotFoundException
	 * @throws InstanceAlreadyExistsException
	 * @throws MBeanException
	 * @throws ReflectionException
	 */
	public static Object invokeMBeanPorTipoComponenteAplicacion(String tipoComponente, String codAplicacion, String metodo, Object[] valoresParametros, String[] tipoParametros) throws MalformedObjectNameException, InstanceNotFoundException, InstanceAlreadyExistsException, MBeanException, ReflectionException {
		return invokeMBeanPorTipoComponenteAplicacion(mBeanServerDefault, tipoComponente, codAplicacion, metodo, valoresParametros, tipoParametros);
	}

	/**
	 * Método para la recuperación de un conjunto de MBeans sobre una instancia, tipo de Componente y 
	 * aplicación concretos.
	 * @param beanServer
	 * @param tipoComponente
	 * @param codAplicacion
	 * @param metodo
	 * @param valoresParametros
	 * @param tipoParametros
	 * @return
	 * @throws MalformedObjectNameException
	 * @throws InstanceNotFoundException
	 * @throws InstanceAlreadyExistsException
	 * @throws MBeanException
	 * @throws ReflectionException
	 */
	private static Object invokeMBeanPorTipoComponenteAplicacion(MBeanServer beanServer, String tipoComponente, String codAplicacion, String metodo, Object[] valoresParametros, String[] tipoParametros) throws MalformedObjectNameException, InstanceNotFoundException, InstanceAlreadyExistsException, MBeanException, ReflectionException {
		Set listaNombres = beanServer.queryNames(Q70MBeanBase.getNombreMBeanAplicacion(tipoComponente,codAplicacion),null);
		if (listaNombres.isEmpty()){
			throw new InstanceNotFoundException(Q70MBeanBase.getNombreMBeanAplicacion(tipoComponente,codAplicacion).toString());
		}else{
			if (listaNombres.size()>1){
				throw new InstanceAlreadyExistsException(Q70MBeanBase.getNombreMBeanAplicacion(tipoComponente,codAplicacion).toString());
			}else{
				return beanServer.invoke((ObjectName)listaNombres.iterator().next(), metodo, valoresParametros, tipoParametros);
			}
		}
	}

	/**
	 * Método que devuelve una lista de instancias pertenecientes a un Cluster pasado como parámetro.
	 * @param listaInstancias disponibles en BBDD
	 * @param nbrCluster nombre del Cluster del que deseamos obtener sus instancias
	 * @return
	 */
	private static List getInstanciasCluster (List listaInstancias, String nbrCluster) {
		List listaInstanciasCluster = new ArrayList();
		Iterator it = listaInstancias.iterator();
		Q70Instancia instancia = null;
		while (it.hasNext()){
			instancia = (Q70Instancia)it.next();
			if ((instancia.getNombreCluster() != null) && (instancia.getNombreCluster().equals(nbrCluster))){
				listaInstanciasCluster.add(instancia);
			}
		}
		return listaInstanciasCluster;
	}

	private void doTrazaError(Throwable exception, String strMensaje) {
		Date fechaHora = new Date();
		String mensaje = fechaHora  + ":" + CODIGO_APLICACION_FRAMEWORK + ":" + ID_COMPONENTE + ":" + strMensaje + "\n "
			+ exception.getLocalizedMessage();
		if (Q70LogInterno.isEnabled())
			Q70LogInterno.log(mensaje);
		else{
			System.err.println(mensaje);
			System.err.println("***** INICIO: TRAZA EXCEPCION ("+fechaHora+") *****");
			exception.printStackTrace();
			System.err.println("***** FIN: TRAZA EXCEPCION ("+fechaHora+") *****");
		}
		Q70GestorDeExcepcionesServidor.getSingleton().recogerThrowable(new Q70FrameworkException(ID_COMPONENTE,this.getClass().toString(),"contextInitialized","Se ha producido error al inicializar la aplicacion.",exception));
	}
}