Development

Gestion à distance des EJB (Entreprise JavaBeans) - JBoss 6 - Wildfly

Nous avons effectué un appel EJB à distance indépendamment du chargeur de classes du serveur d’application afin de résoudre les problèmes de communication JBoss6/Wildfly9.

August 12 2016

Problème

La capacité d'appeler des EJB à distance à travers différentes versions de JBoss/Wildfly AS n'est pas prise en charge.

Pour appeler des EJB à distance, vous avez besoin d'une bibliothèque client. La bibliothèque client contient des classes qui font normalement partie du serveur d'application. Lorsque le client est une application autonome (Java SE), c'est une solution adéquate, mais lorsque le client est un autre serveur d'application Java EE, cette solution est inappropriée car de nombreuses classes entrent en conflit.

La seule exception se produit lorsque les serveurs d'application sont exactement de la même version, car aucune bibliothèque client n'est alors requise.

Solution

Essayez d'imiter un client EJB autonome pour le serveur cible.

Passez un appel EJB à distance indépendamment du chargeur de classes du serveur d'application, et n'ajoutez que les bibliothèques client requises à partir d'un autre serveur.

Trouver la solution

En cherchant une solution, nous avons découvert un blog de Carlo de Wolf (RedHat) qui traite de problèmes similaires. La partie la plus importante portait sur la classe AluniteClassLoader, qui ne possède pas de chargeur de classes parent, et qui à son tour délègue à des chargeurs de classes donnés.

Il était aussi important d'inclure correctement votre propre interface afin qu'elle soit accessible au chargeur de classes, pour éviter les ClassCastExceptions si redoutées.

Démonstration du concept

Projet de communication entre serveurs (https://github.com/infobip/jboss-wildfly-remoting)

  • 1 JAR module – cross-server-common, avec interface à distance IConnector, et classe JndiHandler
  • 1 EJB module – cross-server-ejb-jboss6, avec une installation d'IConnector, destiné à JBoss6
  • 1 EJB module – cross-server-ejb-wildfly9, avec une installation d'IConnector, destiné à Wildfly9
  • 1 EAR module – construit à partir d'un profile:
    • Le profile Wildfly9 construit un EAR avec cross-server-common et cross-server-ejb-wildfly9
    • Le profile jboss6 construit un EAR avec cross-server-common et cross-server-ejb-jboss6, et exclut le descripteur de déploiement jboss-deployment-structure.xml

Blog Graphic

L'interface IConnector a deux méthodes : hello() et answer(). Chaque hello() appelle answer() sur une autre instance de serveur.

La classe JndiHandler a trois méthodes : une pour la recherche standard sur JBoss6, et deux méthodes pour Wildfly9 - pour la recherche basée sur une API pour le client EJB, et pour la recherche de style http à distance sur un projet 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 vous choisissez d'utiliser la recherche client EJB, le client doit avoir jboss-ejb-client.properties sur 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);

Chaque serveur doit avoir des bibliothèques client à partir d'un autre serveur sur une machine locale, mais qui ne soit pas déployé sur un serveur d'application.

Lançons deux serveurs d'application.

Nous choisissons de lancer JBoss6 AS sur un port 8080 standard, et Wildfly avec une valeur de décalage des ports à 100.

Communication JBoss6-Wildfly9 avec la méthode 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);

Communication Wildfly9-Jboss6 avec la méthode 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); 

Et la magie opère...

Lorsque nous lançons un test Junit dans cross-server-wildfly9:

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

Engineering

Engineering

Et le même test Junit dans cross-server-jboss6:

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

Engineering

Engineering

((par Jelena Lazic, ingénieur logiciel))