package es.ejie.frmk.negocio.adaptadorinvocacion;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import es.ejie.frmk.Q70Factoria;
import es.ejie.frmk.infraestructura.poolCache.Q70Constantes;
import es.ejie.frmk.infraestructura.poolCache.Q70GestorCacheServidorInterface;

/**
 * La clase se encarga de proporcionar los interfaces <code>home</code> de ejbs que representan <b>funciones de integración</b>
 * a partir de su nombre JNDI.
 * <p>
 * Se incorpora una caché que permite almacenar los interfaces home de los EJBs indexados mediante un código hash
 * de tal forma que una vez solicitado el home de un ejb, las posteriores solicitudes las devuelve directamente de la caché.
 * <p>
 * Los desarrolladores de las aplicaciones pueden utilizar esta clase para invocar Servicios de Negocio (EJB entity) desde
 * una Función de Integración (método de un EJB session)
 * La forma de trabajar con la factoría de Homes es la siguiente:
 * <P>
 * <code>home = FactoríaEJBHome.getSingleton().getHome(ejbName);</code>
 * <P>
 * Esta clase también es utilizada por la clase <code>Q70AdaptadorDeInvocacion</code> de Geremua para localizar el 
 * EJB de sesión que implementa el método que hace las veces de Función de Integración. 
 */
public class Q70FactoriaEJBHome
{
	private static final String LOCAL_INTERFACE_PREFIX = "local:ejb/";
	// String de concatenacion para almacenar las home locales en el Gestor de Cache
	private static final String JNDI_NAME_PREFIX_LOCAL = "Q70FactoriaEJBHome_DATA_LOCAL_";
	/*
	 * En la versión 2.0 de Geremua, no cacheamos los interfaces remotos ya
	 * que cuando redesplegamos una aplicación remota se quedarían cacheados
	 * los interfaces de sus EJB en nuestra caché y nos darían error.
	 * Fecha: 03-feb-2006
	 * 
	// String de concatenacion para almacenar las home remotas en el Gestor de Cache
	private static final String JNDI_NAME_PREFIX_REMOTE = "Q70FactoriaEJBHome_DATA_REMOTE_";*/
	
	// instancia única del objecto (Singleton)
	private static Q70FactoriaEJBHome ejbHomeCache = null;
	//referencia al contexto jndi
	private Context context;

	private Q70FactoriaEJBHome() throws NamingException
	{
		//obtener el contexto jndi
		context = getInitialContext();		
	}

	/**
	 * Proporciona acceso a la instancia única de la clase <code>Q70FactoriaEJBHome</code>, 
	 * que sigue el patrón Singleton.
	 * <p>
	 * 
	 * @return Devuelve la instancia única de <code>Q70FactoriaEJBHome</code> 
	 */
	public static Q70FactoriaEJBHome getSingleton() throws NamingException
	{
		//devolver la única instancia de la clase
		if (ejbHomeCache == null)
		{
			ejbHomeCache = new Q70FactoriaEJBHome();
		}
		return ejbHomeCache;
	}

	/**
	 * El método obtiene la factoria (home) ejb de forma transparente para el desarrollador.
	 * <p>
	 * Para ello, implementa el siguiente algoritmo de busqueda:
	 * <UL>
	 * <li>	Realizar la búsqueda primero en la cache.</li>
	 * <li> Si no está en la caché buscar por jndi.</li>
	 * <UL>
	 * <li>	Primero buscar por jndi el LocalHome.</li>
	 * <li> Si no está en local, entonces buscar el Home remoto.</li>
	 * </UL>
	 * <li> Si se ha encontrado, poner en caché para posteriores usos.</li>
	 * <li> Devolver el Home.</li>
	 * </UL>
	 * 
	 * @param jndiName El nombre del interfaz remoto tal y como se especifica en el deployment descriptor 
	 * @return La referencia al home del ejb o null si no se encontró.
	 * 
	 * @throws NamingException
	 */
	public Object getHome(String jndiName) throws NamingException
	{
		Object home = null;
		
		Q70GestorCacheServidorInterface gestorCache = (Q70GestorCacheServidorInterface)Q70Factoria.getGestorCache();

		// Realizar la busqueda primero en la cache.
		home = gestorCache.getElement(Q70Constantes.CACHE_EJB_HOMES, JNDI_NAME_PREFIX_LOCAL + jndiName);
		
		//Si no está en la caché buscarlo con jndi.
		if (home == null)
		{
			//sentencia por si fuera necesario hacer el cast portable para rmi-iiop
			//home = (EJBHome) PortableRemoteObject.narrow(getInitialContext().lookup(homeClass.getName()),homeClass);
			
			//realizar la busqueda en primer lugar el LocalHome
			try
			{
				home = lookup(LOCAL_INTERFACE_PREFIX + jndiName);
			}
			catch(NamingException ne){}
			
			//Si no está en local entonces buscar el Home remoto.
			if (home == null){
				home = lookup(jndiName);
			}
			//Si se ha encontrado
			if (home != null){
				// Añadimos el home a la caché
				gestorCache.addElement(Q70Constantes.CACHE_EJB_HOMES, JNDI_NAME_PREFIX_LOCAL + jndiName, home);
			}
		}

		return home;
	}
	
	/**
	 * El método obtiene la factoria (home) ejb de forma transparente para el desarrollador cuando ésta es remota y
	 * se encuentra en otra máquina.
	 * <p>
	 * <UL>
	 * <li> Devolver el Home.</li>
	 * </UL>
	 * 
	 * @param jndiName El nombre del interfaz remoto tal y como se especifica en el deployment descriptor
	 * @param initialCtxFactory InitialContextFactory de la máquina remota ("weblogic.jndi.WLInitialContextFactory") 
	 * @param providerURL Provider URL para la obtención del contexto remoto ("t3://localhost:7001")
	 * @return La referencia al home del ejb o null si no se encontró.
	 * 
	 * @throws NamingException
	 */
	public Object getRemoteHome(String jndiName, String initialCtxFactory, String providerURL) throws NamingException{
		/*
		 * En la versión 2.0 de Geremua, no cacheamos los interfaces remotos ya
		 * que cuando redesplegamos una aplicación remota se quedarían cacheados
		 * los interfaces de sus EJB en nuestra caché y nos darían error.
		 * Fecha: 03-feb-2006
		 * 
		Object home = null;
		
		//Reviso si está en la Caché
		home = Q70GestorCache.getSingleton().getElement(JNDI_NAME_PREFIX_REMOTE + providerURL + jndiName);
		
		//Si no está en la caché buscarlo con jndi.
		if (home == null)
		{
			//sentencia por si fuera necesario hacer el cast portable para rmi-iiop
			//home = (EJBHome) PortableRemoteObject.narrow(getInitialContext().lookup(homeClass.getName()),homeClass);
			
			home = lookupRemote(jndiName, initialCtxFactory, providerURL);
			//Almaceno su entrada en la caché			
			Q70GestorCache.getSingleton().addElement(JNDI_NAME_PREFIX_REMOTE + providerURL + jndiName, home);
		}

		return home;*/
		
		return lookupRemote(jndiName, initialCtxFactory, providerURL);
	}
	
	private Object lookup(String jndiName) throws NamingException
	{
		return context.lookup(jndiName);
	}

	private Context getInitialContext() throws NamingException
	{
		return new InitialContext();	
	}
	
	/*
	 * Realiza la búsqueda de una Home en un contexto remoto. 
	 * Si se produce un error en la obtención del contexto o en el lookup se lanzará un NamingException 
	 * */
	private Object lookupRemote(String jndiName, String initialCtxFactory, String providerURL) throws NamingException
	{
		//Creamos el contexto remoto
		java.util.Hashtable prop = new java.util.Hashtable();
		prop.put(Context.INITIAL_CONTEXT_FACTORY, initialCtxFactory);
		prop.put(Context.PROVIDER_URL, providerURL);
		
		Context context = new InitialContext(prop);
		
		return context.lookup(jndiName);
	}
}
