Hi,

In JBossAS7 or in WildFly there are different approaches which we can use in order to access EJBs. Either using “EJB client API” or using “remote naming” approach. EJB Client API is a WildFly-specific API that allows invocation on remote EJBs. This client API isn’t based on JNDI. So remote clients need not rely on JNDI API to invoke on EJBs.

Using EJB Client API based approach for EJB lookup is the recommended approach. If the remote-naming is used there are some restrictions as there is no full support of the ejb-client features. Following are some points which encourages us to use the EJB Client API based approach over the remote-naming based approach for EJB invocations.

1. No load balancing, if the URL contains multiple “remote://” servers there is no load balancing, the first available server will be used and only in case it is not longer available there will be a failover to the next available one.

2. No cluster support. As a cluster needs to be defined in the “jboss-ejb-client.properties” this feature can not be used and there is no cluster node added

3. No client side interceptor. The EJBContext.getCurrent() can not be used and it is not possible to add a client interceptor

4. No UserTransaction support

5. All proxies become invalid if .close() for the related javax.naming.InitalContext is invoked, or the InitialContext is not longer referenced and gets garbage-collected. In this case the underlying EJBContext is destroyed and the connections are closed.

6. It is not possible to use remote-naming if the client is an application deployed on another JBoss instance

What this demo is about ?

This example demonstrates why to use the EJB Client API and not the remote naming based API. As part of this sample we will see how the EJB Client can automatically gets reconnected to WildFly automatically if the wildly instance goes down and rebooted again.

So lets start developing a very simple EJB3 application and then deploy it on WildFly 10 (WildFly 8/9 also can be used).

Developing and deploying EJB3 in WildFly

Step-1). Open a terminal and then move inside the WildFly 10 “bin” directory and then create “ejbuser” (with password “ejbuser@1”) in the ApplicationRealm as following:

   $ cd /PATH/TO/wildfly-10.0.0.CR3-SNAPSHOT/bin

   $ ./add-user.sh -a ejbuser ejbuser@1
      Updated user 'ejbuser' to file ‘/PATH/TO/wildfly-10.0.0.CR3-SNAPSHOT/standalone/configuration/application-users.properties'
      Updated user 'ejbuser' to file ‘/PATH/TO/wildfly-10.0.0.CR3-SNAPSHOT/domain/configuration/application-users.properties'

Step-2). Start the WildFly instance normally as following:

   $ cd /PATH/TO/wildfly-10.0.0.CR3-SNAPSHOT/bin
   $ ./standalone.sh 

Step-3). Lets now create a directory where we will place our EJB remote interface, EJB Bean and the clients.

   $ mkdir  WildFly_EJB_Client_Reconnect
   $ mkdir -p WildFly_EJB_Client_Reconnect/src
   $ cd /PATH/TO/WildFly_EJB_Client_Reconnect

Developing EJB Project

Step-4). First of all we will write EJB remote interface “CallerRemote.java” inside the “WildFly_EJB_Client_Reconnect/src” directory as following:

package ejb3;
public interface CallerRemote {
     public String testMethod(String name);
}

Step-5). Now we will write EJB Stateless Bean “CallerBean.java” inside the “WildFly_EJB_Client_Reconnect/src” directory as following:

package ejb3;
import javax.ejb.*;
import javax.naming.*;
import java.util.*;
@Stateless  
@Remote(CallerRemote.class)
public class CallerBean implements CallerRemote {
     public String testMethod(String name) {
          String result="";
		  System.out.println("\n\n\t[CallerBean] Bean's testMethod(String name) called...."); 
		  return "[CallerBean] returned Welcome, "+name;
	 }
 }

Step-6). As we want to package our above mentioned EJBs inside an EAR file so lets create an “application.xml” file which will be our EAR deployment descriptor. So lets create the following kind of application.xml file inside the “WildFly_EJB_Client_Reconnect/src”

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
             xmlns="http://java.sun.com/xml/ns/javaee" 
             xmlns:application="http://java.sun.com/xml/ns/javaee/application_5.xsd" 
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_5.xsd" 
             id="Application_ID" version="5">
  <module>
    <ejb>remoteEjbOne.jar</ejb>
  </module>
</application>

Writing EJB Client API approach based Client

Step-7). As we are going to use the EJB Client API based approach hence lets first create the “jboss-ejb-client.properties” as following inside the “WildFly_EJB_Client_Reconnect/src”, In this file we will define our WildFly server details where our EJB is deployed. While running this code we will need to make sure that this file is present in client CLASSPATH along with the “$JBOSS_HOME/bin.client/jboss-client.jar” file.

remote.connections=one
remote.connection.one.host=localhost
remote.connection.one.port=8080
remote.connection.one.username=ejbuser
remote.connection.one.password=ejbuser@1
remote.connection.one.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false

Step-8). Now the most important of writing the EJB Client based code. So lets write the “Client.java” as following inside the “WildFly_EJB_Client_Reconnect/src”:

package client;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Properties;
import ejb3.CallerRemote;
public class Client {
     public static void main(String ar[]) throws Exception {
          Context context=null;
          try {
                Properties props = new Properties();
                props.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
                context = new InitialContext(props);	
	            System.out.println("\n\tGot initial Context: "+context);		
          } catch (Exception e) {
                e.printStackTrace();
          }

          //   When the invocation starts you try stopping the WildFly and then restart it back to find out if client reconnection works or not.
          CallerRemote remote=(CallerRemote)context.lookup("ejb:TestEAR/remoteEjbOne/CallerBean!ejb3.CallerRemote");
          for (int i = 0; i <= 300; i++) {  
             try {
                   System.out.println("\n\t remote.testMethod(\"MiddlewareMagic!!!\") = "+remote.testMethod("MiddlewareMagic!!!"));
                   Thread.sleep(500);
              } catch(Exception e) {                    
                    System.out.println("\n\tEXCEPTION: " + e.getMessage());
                    e.printStackTrace();
                    try{ Thread.sleep(500); } catch(InterruptedException ee) {  ee.printStackTrace(); } 
              }  
          }  
     }
  }

Writing ANT script to build/deploy/run

Step-9). Now we will write the following kind of ANT build script which will build The EAR containing the EJBs, deploy it on WildFly as well as will run the client. So lets create the “build.xml” file inside the “WildFly_EJB_Client_Reconnect” directory as following:

<project name="SingletonStartupService" default="all">
<property name="jboss.home" value="/Users/jsensharma/NotBackedUp/Installed/wildfly-10.0.0.CR3-SNAPSHOT" />
<property name="jboss.module.dir" value="${jboss.home}/modules" />
<property name="basedir" value="." />
<property name="tmp.dir" value="tmp" />
<property name="src.dir" value="src" />
<property name="output.dir" value="test" />
<property name="application.dir" value="build" />
<property name="ear.name" value="TestEAR.ear" />
<property name="ejb.one.jar" value="remoteEjbOne.jar" />
<property name="client.jar.name" value="remoteEJBClient.jar" />

        <path id="jboss.classpath">
           <fileset dir="${jboss.module.dir}/system/layers/base/javax/ejb/api/main">
               <include name="jboss-ejb-api*.jar"/>
           </fileset>  
        </path>

        <!-- Client Needs the following Jar to be present in the CLASSPATH including -->
        <path id="jboss.new.client.classpath">
           <fileset dir="${jboss.home}/bin/client">
               <include name="jboss-client.jar" />
           </fileset>  
        </path>
	 
        <target name="all" depends="buildEAR,message" />

        <target name="buildEAR" >
           <delete dir="${tmp.dir}" />
           <mkdir dir="${tmp.dir}/META-INF" />
           <mkdir dir="${application.dir}" />
           <javac srcdir="${src.dir}" destdir="${tmp.dir}"  includes="Caller*.java" classpathref="jboss.classpath"/>
           <copy file="${src.dir}/CallerBean.java" todir="${tmp.dir}/ejb/one"/>
           <copy file="${src.dir}/CallerRemote.java" todir="${tmp.dir}/ejb/one"/>
           <jar jarfile="${output.dir}/${ejb.one.jar}" basedir="${tmp.dir}" compress="true" />

           <mkdir dir="${output.dir}/META-INF"/>
           <copy todir="${output.dir}/META-INF">
                <fileset dir="${src.dir}/">
                  <include name="application.xml"/> 
                </fileset>
           </copy>           
           <jar jarfile="${application.dir}/${ear.name}" basedir="${output.dir}" compress="true" />
           <delete includeEmptyDirs="true">
              <fileset dir="${output.dir}"/>
           </delete> 
           <delete dir="${tmp.dir}" />
        </target>

        <target name="message" depends="buildEAR">
            <echo message="*******************  ******************* *********************" />  
            <copy file="${application.dir}/${ear.name}" tofile="${jboss.home}/standalone/deployments/${ear.name}"/>
            <echo message="********** ${application.dir}/${ear.name} Build and Deployed Successfully **********" />  
            <echo message="*******************  ******************* *********************" />  
        </target>

        <target name="run">
           <delete dir="${tmp.dir}" />
           <mkdir dir="${tmp.dir}" />
           <javac srcdir="${src.dir}" destdir="${tmp.dir}"  includes="CallerRemote.java,Client.java" classpathref="jboss.classpath"/> 
           <copy file="${src.dir}/CallerRemote.java" todir="${tmp.dir}/ejb3"/>
           <copy file="${src.dir}/Client.java" todir="${tmp.dir}/client"/>    
           <copy file="${src.dir}/jboss-ejb-client.properties" todir="${tmp.dir}"/>     
           <jar jarfile="${application.dir}/${client.jar.name}" basedir="${tmp.dir}" compress="true" />
           <delete dir="${tmp.dir}"/>

           <java classname="client.Client" fork="true">
               <classpath>
                  <pathelement location="${application.dir}/${client.jar.name}"/>
                  <path refid="jboss.new.client.classpath"/>
               </classpath>
           </java>
        </target>        
    
</project>

Step-10). We can run the above ANT based EJB client project simply by setting the ANT_HOME, JAVA_HOME and PATH, So open a command prompt and then run the following commands as following:

For Unix Based OS:

$ export ANT_HOME=/PATH/TO/apache_ant_1.9.2
$ export JAVA_HOME=/PATH/TO/jdk1.8.0_60
$ export PATH=$JAVA_HOME/bin:$ANT_HOME/bin:$PATH
$ cd /PATH/TO/WildFly10_JMS_Client


<strong>For Windows Based OS</strong>
$ set ANT_HOME=C:\PATH\TO\apache_ant_1.9.2
$ set JAVA_HOME=C:\PATH\TO\jdk1.8.0_60
$ set PATH=%JAVA_HOME%\bin;%ANT_HOME%\bin;%PATH%
$ cd C:\WildFly10_JMS_Client

Step-11). Lets deploy the application by running the ANT:

$ ant
Buildfile: /Users/jsensharma/NotBackedUp/MM_Tests/Forums/WildFly/WildFly_EJB_Client_Reconnect/build.xml

buildEAR:
    [mkdir] Created dir: /Users/jsensharma/NotBackedUp/MM_Tests/Forums/WildFly/WildFly_EJB_Client_Reconnect/tmp/META-INF
    [javac] /Users/jsensharma/NotBackedUp/MM_Tests/Forums/WildFly/WildFly_EJB_Client_Reconnect/build.xml:32: warning: 'includeantruntime' was not set, defaulting to build.sysclasspath=last; set to false for repeatable builds
    [javac] Compiling 2 source files to /Users/jsensharma/NotBackedUp/MM_Tests/Forums/WildFly/WildFly_EJB_Client_Reconnect/tmp
     [copy] Copying 1 file to /Users/jsensharma/NotBackedUp/MM_Tests/Forums/WildFly/WildFly_EJB_Client_Reconnect/tmp/ejb/one
     [copy] Copying 1 file to /Users/jsensharma/NotBackedUp/MM_Tests/Forums/WildFly/WildFly_EJB_Client_Reconnect/tmp/ejb/one
      [jar] Building jar: /Users/jsensharma/NotBackedUp/MM_Tests/Forums/WildFly/WildFly_EJB_Client_Reconnect/test/remoteEjbOne.jar
    [mkdir] Created dir: /Users/jsensharma/NotBackedUp/MM_Tests/Forums/WildFly/WildFly_EJB_Client_Reconnect/test/META-INF
     [copy] Copying 1 file to /Users/jsensharma/NotBackedUp/MM_Tests/Forums/WildFly/WildFly_EJB_Client_Reconnect/test/META-INF
      [jar] Building jar: /Users/jsensharma/NotBackedUp/MM_Tests/Forums/WildFly/WildFly_EJB_Client_Reconnect/build/TestEAR.ear
   [delete] Deleting directory /Users/jsensharma/NotBackedUp/MM_Tests/Forums/WildFly/WildFly_EJB_Client_Reconnect/tmp

message:
     [echo] *******************  ******************* *********************
     [copy] Copying 1 file to /Users/jsensharma/NotBackedUp/Installed/wildfly-10.0.0.CR3-SNAPSHOT/standalone/deployments
     [echo] ********** build/TestEAR.ear Build and Deployed Successfully **********
     [echo] *******************  ******************* *********************

all:

BUILD SUCCESSFUL
Total time: 0 seconds

Step-12). Now Run the client using the same Client.

 ant run
Buildfile: /Users/jsensharma/NotBackedUp/MM_Tests/Forums/WildFly/WildFly_EJB_Client_Reconnect/build.xml

run:
    [mkdir] Created dir: /Users/jsensharma/NotBackedUp/MM_Tests/Forums/WildFly/WildFly_EJB_Client_Reconnect/tmp
    [javac] /Users/jsensharma/NotBackedUp/MM_Tests/Forums/WildFly/WildFly_EJB_Client_Reconnect/build.xml:60: warning: 'includeantruntime' was not set, defaulting to build.sysclasspath=last; set to false for repeatable builds
    [javac] Compiling 2 source files to /Users/jsensharma/NotBackedUp/MM_Tests/Forums/WildFly/WildFly_EJB_Client_Reconnect/tmp
     [copy] Copying 1 file to /Users/jsensharma/NotBackedUp/MM_Tests/Forums/WildFly/WildFly_EJB_Client_Reconnect/tmp/ejb3
     [copy] Copying 1 file to /Users/jsensharma/NotBackedUp/MM_Tests/Forums/WildFly/WildFly_EJB_Client_Reconnect/tmp/client
     [copy] Copying 1 file to /Users/jsensharma/NotBackedUp/MM_Tests/Forums/WildFly/WildFly_EJB_Client_Reconnect/tmp
      [jar] Building jar: /Users/jsensharma/NotBackedUp/MM_Tests/Forums/WildFly/WildFly_EJB_Client_Reconnect/build/remoteEJBClient.jar
   [delete] Deleting directory /Users/jsensharma/NotBackedUp/MM_Tests/Forums/WildFly/WildFly_EJB_Client_Reconnect/tmp
      
      	Got initial Context: javax.naming.InitialContext@ae3865e
      Nov 08, 2015 9:13:51 PM org.jboss.ejb.client.EJBClient <clinit>
      INFO: JBoss EJB Client version 2.1.2.Final
      Nov 08, 2015 9:13:51 PM org.xnio.Xnio <clinit>
      INFO: XNIO version 3.3.2.Final
      Nov 08, 2015 9:13:51 PM org.xnio.nio.NioXnio <clinit>
      INFO: XNIO NIO Implementation Version 3.3.2.Final
      Nov 08, 2015 9:13:51 PM org.jboss.remoting3.EndpointImpl <clinit>
      INFO: JBoss Remoting version 4.0.14.Final
      Nov 08, 2015 9:13:52 PM org.jboss.ejb.client.remoting.VersionReceiver handleMessage
      INFO: EJBCLIENT000017: Received server version 2 and marshalling strategies [river]
      
      	 remote.testMethod("MiddlewareMagic!!!") = [CallerBean] returned Welcome, MiddlewareMagic!!!
      Nov 08, 2015 9:13:52 PM org.jboss.ejb.client.remoting.RemotingConnectionEJBReceiver associate
      INFO: EJBCLIENT000013: Successful version handshake completed for receiver context EJBReceiverContext{clientContext=org.jboss.ejb.client.EJBClientContext@6f526c5f, receiver=Remoting connection EJB receiver [connection=org.jboss.ejb.client.remoting.ConnectionPool$PooledConnection@c490a12,channel=jboss.ejb,nodename=banl13bca644a-3]} on channel Channel ID b29f24ca (outbound) of Remoting connection 3af686da to localhost/127.0.0.1:8080
      
      	 remote.testMethod("MiddlewareMagic!!!") = [CallerBean] returned Welcome, MiddlewareMagic!!!
      
      	 remote.testMethod("MiddlewareMagic!!!") = [CallerBean] returned Welcome, MiddlewareMagic!!!
      
      	 remote.testMethod("MiddlewareMagic!!!") = [CallerBean] returned Welcome, MiddlewareMagic!!!
      
      	 remote.testMethod("MiddlewareMagic!!!") = [CallerBean] returned Welcome, MiddlewareMagic!!!

EJB Client reconnection test

Now in order to see if the EJB Client is able to reconnect to the restarted WildFly automatically or not, Let the above EJB client keep running and in the mean time restart the WildFly, We will notice that the EJB Client gets the following exception for sometime until the WildFly is restarted and as soon as the WildFly is completely restarted then the EJB Client API approach based EJB Client automatically reconnects to it.

      	 remote.testMethod("MiddlewareMagic!!!") = [CallerBean] returned Welcome, MiddlewareMagic!!!
      
      	 remote.testMethod("MiddlewareMagic!!!") = [CallerBean] returned Welcome, MiddlewareMagic!!!
      
      	 remote.testMethod("MiddlewareMagic!!!") = [CallerBean] returned Welcome, MiddlewareMagic!!!
      
      	 remote.testMethod("MiddlewareMagic!!!") = [CallerBean] returned Welcome, MiddlewareMagic!!!
      	EXCEPTION: EJBCLIENT000025: No EJB receiver available for handling [appName:TestEAR, moduleName:remoteEjbOne, distinctName:] combination for invocation context org.jboss.ejb.client.EJBClientInvocationContext@b10b9b2
      java.lang.IllegalStateException: EJBCLIENT000025: No EJB receiver available for handling [appName:TestEAR, moduleName:remoteEjbOne, distinctName:] combination for invocation context org.jboss.ejb.client.EJBClientInvocationContext@3d600a0c
      	at org.jboss.ejb.client.EJBClientContext.requireEJBReceiver(EJBClientContext.java:798)
      	at org.jboss.ejb.client.ReceiverInterceptor.handleInvocation(ReceiverInterceptor.java:116)
      	at org.jboss.ejb.client.EJBClientInvocationContext.sendRequest(EJBClientInvocationContext.java:186)
      	at org.jboss.ejb.client.EJBInvocationHandler.sendRequestWithPossibleRetries(EJBInvocationHandler.java:255)
      	at org.jboss.ejb.client.EJBInvocationHandler.doInvoke(EJBInvocationHandler.java:200)
      	at org.jboss.ejb.client.EJBInvocationHandler.doInvoke(EJBInvocationHandler.java:183)
      	at org.jboss.ejb.client.EJBInvocationHandler.invoke(EJBInvocationHandler.java:146)
      	at com.sun.proxy.$Proxy0.testMethod(Unknown Source)
      	at client.Client.main(Unknown Source)
      
      	EXCEPTION: EJBCLIENT000025: No EJB receiver available for handling [appName:TestEAR, moduleName:remoteEjbOne, distinctName:] combination for invocation context org.jboss.ejb.client.EJBClientInvocationContext@3d600a0c
      Nov 08, 2015 9:17:39 PM org.jboss.ejb.client.remoting.VersionReceiver handleMessage
      INFO: EJBCLIENT000017: Received server version 2 and marshalling strategies [river]
      Nov 08, 2015 9:17:39 PM org.jboss.ejb.client.remoting.RemotingConnectionEJBReceiver associate
      INFO: EJBCLIENT000013: Successful version handshake completed for receiver context EJBReceiverContext{clientContext=org.jboss.ejb.client.EJBClientContext@f342a0b, receiver=Remoting connection EJB receiver [connection=org.jboss.ejb.client.remoting.ConnectionPool$PooledConnection@1a8ed249,channel=jboss.ejb,nodename=banl13bca644a-3]} on channel Channel ID fe6d7219 (outbound) of Remoting connection 511533be to localhost/127.0.0.1:8080
      java.lang.IllegalStateException: EJBCLIENT000025: No EJB receiver available for handling [appName:TestEAR, moduleName:remoteEjbOne, distinctName:] combination for invocation context org.jboss.ejb.client.EJBClientInvocationContext@5f51da55
      	at org.jboss.ejb.client.EJBClientContext.requireEJBReceiver(EJBClientContext.java:798)
      	at org.jboss.ejb.client.ReceiverInterceptor.handleInvocation(ReceiverInterceptor.java:116)
      	at org.jboss.ejb.client.EJBClientInvocationContext.sendRequest(EJBClientInvocationContext.java:186)
      	at org.jboss.ejb.client.EJBInvocationHandler.sendRequestWithPossibleRetries(EJBInvocationHandler.java:255)
      	at org.jboss.ejb.client.EJBInvocationHandler.doInvoke(EJBInvocationHandler.java:200)
      	at org.jboss.ejb.client.EJBInvocationHandler.doInvoke(EJBInvocationHandler.java:183)
      	at org.jboss.ejb.client.EJBInvocationHandler.invoke(EJBInvocationHandler.java:146)
      	at com.sun.proxy.$Proxy0.testMethod(Unknown Source)
      	at client.Client.main(Unknown Source)
      
      	EXCEPTION: EJBCLIENT000025: No EJB receiver available for handling [appName:TestEAR, moduleName:remoteEjbOne, distinctName:] combination for invocation context org.jboss.ejb.client.EJBClientInvocationContext@5f51da55
      java.lang.IllegalStateException: EJBCLIENT000025: No EJB receiver available for handling [appName:TestEAR, moduleName:remoteEjbOne, distinctName:] combination for invocation context org.jboss.ejb.client.EJBClientInvocationContext@271d900d
      	at org.jboss.ejb.client.EJBClientContext.requireEJBReceiver(EJBClientContext.java:798)
      	at org.jboss.ejb.client.ReceiverInterceptor.handleInvocation(ReceiverInterceptor.java:116)
      	at org.jboss.ejb.client.EJBClientInvocationContext.sendRequest(EJBClientInvocationContext.java:186)
      	at org.jboss.ejb.client.EJBInvocationHandler.sendRequestWithPossibleRetries(EJBInvocationHandler.java:255)
      	at org.jboss.ejb.client.EJBInvocationHandler.doInvoke(EJBInvocationHandler.java:200)
      	at org.jboss.ejb.client.EJBInvocationHandler.doInvoke(EJBInvocationHandler.java:183)
      	at org.jboss.ejb.client.EJBInvocationHandler.invoke(EJBInvocationHandler.java:146)
      	at com.sun.proxy.$Proxy0.testMethod(Unknown Source)
      	at client.Client.main(Unknown Source)
      
      	EXCEPTION: EJBCLIENT000025: No EJB receiver available for handling [appName:TestEAR, moduleName:remoteEjbOne, distinctName:] combination for invocation context org.jboss.ejb.client.EJBClientInvocationContext@271d900d
      
      	 remote.testMethod("MiddlewareMagic!!!") = [CallerBean] returned Welcome, MiddlewareMagic!!!
      
      	 remote.testMethod("MiddlewareMagic!!!") = [CallerBean] returned Welcome, MiddlewareMagic!!!
      
      	 remote.testMethod("MiddlewareMagic!!!") = [CallerBean] returned Welcome, MiddlewareMagic!!!

.
.
The Source Code for this demo can be found in:
https://github.com/jaysensharma/MiddlewareMagicDemos/tree/master/WildFly/EJB/WildFly_EJB_Client_Reconnect
.
.
Regards
Jay SenSharma

If you enjoyed this post, please consider leaving a comment or subscribing to the RSS feed to have future articles delivered to your feed reader.