Saturday, December 28, 2013

WildFly: EJB invocations from a remote client

Currently I am experimenting with Java EE7 and I have chosen WildFly as my application server. So far everything is working fine, the WebService, EJB 3.2 etc until today when I tried to access my EJB from a client application - jUnit Test.
To solve the issue initially I followed the instruction given in WildFly documentation EJB invocations from a remote client using JNDI, but it is not what I needed. After spending almost half a day at last I achieved what I wanted.

Prerequisite:

  • You need to have an Application User. Follow the instruction given in Add User Utility.
  • I am running my server with the parameter -b 0.0.0.0 --server-config standalone-full.xml
  • jboss-client.jar in your classpath, which can be found inside the /bin/client directory of the server.
Following is the piece of code which you need to access the EJB:

import java.util.Properties;

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

import org.jboss.ejb.client.EJBClient;
import org.jboss.ejb.client.EJBClientContext;
import org.jboss.ejb.client.PropertiesBasedEJBClientConfiguration;
import org.jboss.ejb.client.StatelessEJBLocator;
import org.jboss.ejb.client.remoting.ConfigBasedEJBClientContextSelector;

/**
 * Class EJBLocator is the class to connect with EJB
 * 
 * @author Tapas Bose
 * @since 1.0
 */
public class EJBLocator {

 /**
  * Method locateEJB locates an EJB for the given jndi
  * 
  * @author Tapas Bose
  * @since 1.0
  * @param jndi
  *            - the jndi to lookup
  * @return an instance of EJB
  * @throws NamingException
  */
 @SuppressWarnings("unchecked")
 public static <T> T locateEJB(String jndi) throws NamingException {
  Properties clientProperties = new Properties();
  clientProperties.put("remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED", "false");
  clientProperties.put("remote.connections", "default");
  clientProperties.put("remote.connection.default.port", myPort);
  clientProperties.put("remote.connection.default.host", myHost);
  clientProperties.put("remote.connection.default.username", myUser);
  clientProperties.put("remote.connection.default.password", myPassword);
  clientProperties.put("remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS", "false");

  EJBClientContext.setSelector(new ConfigBasedEJBClientContextSelector(new PropertiesBasedEJBClientConfiguration(clientProperties)));

  Properties properties = new Properties();
  properties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
  Context context = new InitialContext(properties);
  return (T) context.lookup(jndi);
 }

 /**
  * Method locateEJBStateless locates an Stateless EJB for the given parameters
  * 
  * @author Tapas Bose
  * @since 1.0
  * @param viewType
  *            - the view type
  * @param appName
  *            - the application name
  * @param moduleName
  *            - the module name
  * @param beanName
  *            - the bean name
  * @param distinctName
  *            - the distinct name
  * @return an instance of EJB
  */
 public static <T> T locateEJBStateless(Class<T> viewType, String appName, String moduleName, String beanName, String distinctName) {
  Properties properties = new Properties();

  properties.put("endpoint.name", "client-endpoint");
  properties.put("remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED", "false");
  properties.put("remote.connections", "default");
  properties.put("remote.connection.default.port", myPort);
  properties.put("remote.connection.default.host", myHost);
  properties.put("remote.connection.default.username", myUser);
  properties.put("remote.connection.default.password", myPassword);
  properties.put("remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS", "false");

  EJBClientContext.setSelector(new ConfigBasedEJBClientContextSelector(new PropertiesBasedEJBClientConfiguration(properties)));
  StatelessEJBLocator<T> locator = new StatelessEJBLocator<T>(viewType, appName, moduleName, beanName, distinctName);
  T ejb = EJBClient.createProxy(locator);
  return ejb;
 }
}

Use this class as:
YourService service = EJBLocator.locateEJB(jndi);
//where the jndi is of the form ejb:<app-name>/<module-name>/<distinct-name>/<bean-name>!<fully-qualified-classname-of-the-remote-interface>
Or by:
YourServic service = EJBLocator.locateEJBStateless(YourServic.class, "appName", "moduleName", "YourServicImpl", "distinctName");
Hope it will help.

5 comments:

  1. Hi Tapas,

    Im getting this error calling locateEJBStateless:

    Caused by: java.lang.ClassCastException: org.xnio.SingleOption cannot be cast to org.xnio.Option
    at org.xnio.Option.fromString(Option.java:168) [jboss-client.jar:8.0.0.Final]
    at org.xnio.OptionMap$Builder.parseAll(OptionMap.java:313) [jboss-client.jar:8.0.0.Final]
    at org.jboss.ejb.client.PropertiesBasedEJBClientConfiguration.getOptionMapFromProperties(PropertiesBasedEJBClientConfiguration.java:240) [jboss-ejb-client-2.0.0.Final.jar:2.0.0.Final]
    at org.jboss.ejb.client.PropertiesBasedEJBClientConfiguration.parseProperties(PropertiesBasedEJBClientConfiguration.java:227) [jboss-ejb-client-2.0.0.Final.jar:2.0.0.Final]
    at org.jboss.ejb.client.PropertiesBasedEJBClientConfiguration.(PropertiesBasedEJBClientConfiguration.java:129) [jboss-ejb-client-2.0.0.Final.jar:2.0.0.Final]
    ....

    Any idea?

    ReplyDelete
  2. It would be also nice to mention that WildFly uses port 8080 by default for remoting and client libraries are available in maven bom:


    org.wildfly
    wildfly-ejb-client-bom
    pom
    8.0.0.Final

    ReplyDelete
  3. Is there a solution for this problem - Caused by: java.lang.ClassCastException: org.xnio.SingleOption cannot be cast to org.xnio.Option?

    ReplyDelete
    Replies
    1. Hi, Did your problem got solved? I am facing same issue with wildfly10.

      Delete
  4. EJBClientContext causes the TCCL to be used in calls from it's static initialiser. This is bad because the class loading could be triggered from any other code, so the TCCL is not deterministic.

    EJBClientContext's static initialiser calls EJBClientPropertiesLoader.loadEJBClientProperties() and the PropertiesBasedEJBClientConfiguration constructor, both of which use the TCCL via their getClientClassLoader() methods.

    It is not safe to use the TCCL from the static initialiser, since it could be set to anything, depending on what happened to trigger loading the EJBClientContext class.

    ReplyDelete