Development

Удалённый вызов EJB – JBoss 6 – Wildfly

Мы сделали удалённый вызов EJB из загрузчика классов сервера приложения для решения коммуникационных проблем JBoss6/Wildfly9.

December 28 2015

Проблема

Возможность удалённого вызова EJB для различных версий JBoss/Wildfly AS не поддерживается.

Для удалённого вызова EJB, вам необходима клиентская библиотека. Она содержит классы, которые обычно являются частью сервера. Когда клиент – это автономное (Java SE) приложение, то такое решение верно, однако если клиентом является сервер приложения Java EE , то такое невозможно, поскольку многие классы противоречат друг другу.

Единственное исключение, когда сервер приложения будет иметь такую же версию – тогда в клиентской библиотеке нет необходимости.

Решение

Попробуйте имитировать работу автономного EJB клиента для целевого сервера.

Сделайте удалённый вызов EJB без обращения к загрузчику классов сервера приложения, и добавьте необходимые клиентские библиотеки с другого сервера.

Путь к решению

В поисках решения мы наткнулись на блог Карло де Вульфа (RedHat) (http://wolf-71.blogspot.rs/2010/02/et-phone-home.html), в котором устраняется схожая проблема. Основная идея – это класс AluniteClassLoader, у которого нет родительского загрузчика класса, и который, в свою очередь, работает только с указанными загрузчиками классов.

Также важно корректно включить ваш собственный интерфейс, чтобы у него был доступ к загрузчику классов, и при этом избежать конфликта классов ClassCastExceptions.

Обоснование концепции

Кросс-серверный коммуникационный проект (https://github.com/infobip/jboss-wildfly-remoting)

  • 1 JAR модуль – cross-server-common, с удаленным интефейсом IConnector, и классом JndiHandler
  • 1 EJB модуль – cross-server-ejb-jboss6, с применением IConnector, работающий под JBoss6
  • 1 EJB module – cross-server-ejb-wildfly9, с применением IConnector, работающий с Wildfly9
  • 1 EAR модуль – работа различных профилей:
    • профиль wildfly9 создает связь с cross-server-common, и cross-server-ejb-wildfly9
    • профиль jboss6 создает связь cross-server-common, и cross-server-ejb-jboss6 и исключает дескриптор развертывания jboss-deployment-structure.xml

Blog Graphic

Интерфейс IConnector обладает двумя методами, hello() и answer(). Каждый hello() вызывает answer() на другом сервере.

Класс JndiHandler обладает тремя методами: один для стандартного поиска JBoss6, и два метода для Wildfly9 - для поиска EJB-клиента на основании API, и для удаленного http поиска на основании проекта 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);

Если вы выберете использование поиска для EJB- клиента, у него должны быть указаны свойства классов jboss-ejb-client.properties.

  • 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);

Каждый сервер должен иметь клиентские библиотеки с другого сервера на локальной машине, но не разворачивать их на сервере приложения.

Давайте запустим два сервера приложения.

Допустим, мы запустили JBoss6 AS на стандартном порте 8080, и Wildfly со смещенным портом port-offset 100.

Коммуникация JBoss6-Wildfly9 по методу 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);

Коммуникация Wildfly9-Jboss6 по методу 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); 

И магия…

Когда мы проводим Junit тесты на cross-server-wildfly9:

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

Engineering

Engineering

And similar, Junit test in cross-server-jboss6:

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

Engineering

Engineering

(Автор: Jelena Lazic, Разработчик ПО)