Development

EJB remoto, JBoss6 y Wildfly

Hicimos una llamada al EJB remoto en forma aislada del cargador de clases del servidor de la aplicación para resolver problemas de comunicación con JBoss6 y Wildfly9.

December 28 2015

Problema

No es compatible la habilidad de llamar al EJB remoto entre las diferentes versiones de los servidores de aplicaciones JBoss/Wildfly.

Para poder llamar al EJB remoto, necesitamos una librería del lado del cliente. Esta librería del lado del cliente contiene clases que son generalmente parte del servidor de aplicaciones. Cuando un cliente es una aplicación independiente (Java SE), esta es la solución correcta, pero, cuando el cliente es otro servidor de aplicaciones Java EE, esto no es posible, ya que colisionan varias clases.

La única excepción se da cuando los servidores de aplicaciones tienen exactamente la misma versión, ya que no se necesita librería del lado del cliente en ese caso.

Solución

Intente emular un cliente EJB independiente para el servidor de destino.

Realice una llamada al EJB remoto de forma aislada del cargador de clases del servidor de aplicaciones y agregue solo las librerías del lado del cliente necesarias desde otro servidor.

Camino a la solución

Al buscar una solución, nos encontramos con el blog de Carlo de Wolf (RedHat) (http://wolf-71.blogspot.rs/2010/02/et-phone-home.html) que trata un problema similar. La parte más importante es que la clase AluniteClassLoader no tiene cargador de clase padre y lo delega a los cargadores de clase dados de a uno por vez.

También es muy importante la manera correcta de incluir nuestra propia interfaz para que sea accesible para el cargador de clase y así evitar los temidos ClassCastExceptions.

Prueba del concepto

Proyecto de comunicación de servidores cruzados (https://github.com/infobip/jboss-wildfly-remoting)

  • 1 Módulo JAR: cross-server-common, con interfaz remota IConnector y clase JndiHandler.
  • 1 módulo EJB: cross-server-ejb-jboss6, con una implementación IConnector, orientado a JBoss6.
  • 1 módulo EJB: cross-server-ejb-wildfly9, con una implementación IConnector, orientado a Wildfly9.
  • 1 módulo EAR: construcción basada en perfil:
    • perfil de wildfly9 construye ear con cross-server-common y cross-server-ejb-wildfly9
    • perfil de jboss6 construye ear con cross-server-common y cross-server-ejb-jboss6 y excluye el descriptor de implementación jboss-deployment-structure.xml del ear

Blog Graphic

La interfaz IConnector tiene dos métodos, hello() y answer(). Cada hello() llama a answer() en otra instancia de servidor.

La clase JndiHandler tiene 3 métodos: uno para la búsqueda JBoss6 estándar y dos métodos para Wildfly9, para la búsqueda basada en API para cliente EJB y para búsqueda de estilo remoto http basada en el proyecto jboss-remote-naming::

  • lookupJboss6
Properties properties = new Properties(); 
properties.setProperty("java.naming.provider.url", host + ":" + port); 
properties.setProperty("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory"); 
properties.setProperty("java.naming.factory.url.pkgs", "org.jboss.naming:org.jnp.interfaces"); 
InitialContext ctx = new InitialContext(properties); 
T service = (T) ctx.lookup(jndiName);
  • lookupWildfly9 (EJB client API)
Properties properties = new Properties(); 
properties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming"); 
InitialContext ctx = new InitialContext(properties); 
Context ejbRootNamingContext = (Context) ctx.lookup("ejb:");
T service = (T) ctx.lookup(ejb:" + jndiName);

Si elige usar la búsqueda de cliente EJB, el cliente debe tener jboss-ejb-client.properties en el classpath.

  • lookupWildfly9HttpRemoting (jboss-remote-naming)
Properties properties = new Properties(); 
properties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory"); 
properties.put(Context.PROVIDER_URL, "http-remoting://" + host + ":" + port); 
properties.put("jboss.naming.client.ejb.context", true); 
InitialContext ctx = new InitialContext(properties); 
T service = (T) ctx.lookup(jndiName);

Cada servidor necesita tener librerías del lado del cliente de otro servidor en la máquina local, pero no desplegado en el servidor de aplicaciones.

Comencemos con dos servidores de aplicaciones.

Digamos que comenzamos un servidor de aplicaciones JBoss6 con puerto 8080 estándar y un Wildfly con port-offset 100.

Comunicación JBoss6-Wildfly9 en método hello:

ClassLoader previous = Thread.currentThread().getContextClassLoader(); 
URLClassLoader urlCl = new URLClassLoader(new URL[]{ new URL(new File(jbossHome).toURI().toURL(), "client/jboss-client.jar") }, null); 
ClassLoader cl = new AluniteClassLoader(urlCl, previous);
Thread.currentThread().setContextClassLoader(cl);
String jndiname = "/cross-server-test/cross-server-ejb-wildfly9/ConnectorWildfly9Impl!com.infobip.crossserver.IConnector"; 
IConnector connector = (IConnector)JndiHandler.lookupWildfly9HttpRemoting(jndiname, new String[] {"localhost"}, 8180);
String answer = connector.answer(name);
logger.info("Wildfly is answering: " + answer);
// in the end, we return to previous classloader 
Thread.currentThread().setContextClassLoader(previous);

Comunicación JBoss6-Wildfly9 en método hello:

ClassLoader previous = Thread.currentThread().getContextClassLoader(); 
URLClassLoader urlCl = new URLClassLoader(new URL[]{ new URL(new File(jbossHome).toURI().toURL(), "jbossall-client.jar") }, null); 
ClassLoader cl = new AluniteClassLoader(urlCl, previous);
Thread.currentThread().setContextClassLoader(cl);
String jndiname = "ConnectorJBoss6"; 
IConnector connector = (IConnector)JndiHandler.lookupJBoss6(jndiname, new String[] {"localhost"}, JndiHandler.LOCAL_JNDI_PORT);
String answer = connector.answer(name);
logger.info("JBoss6 is answering: " + answer);
// in the end, we return to previous classloader 
Thread.currentThread().setContextClassLoader(previous); 

Y la magia ocurre...

Cuando ejecutamos el test de Junit en cross-server-wildfly9:

IConnector connector = (IConnector) lookup ("localhost", "8180"); connector.hello("Infobip");

Engineering

Engineering

Y, de manera similar, el test Junit en cross-server-jboss6:

IConnector connector = (IConnector) lookup("localhost", "8080"); connector.hello("Infobip");

Engineering

Engineering

(Por Jelena Lazic, ingeniera en software)