Hi,

JBoss AS7 provides a very slight variation in invoking the EJBs remotely from the client side. The EJB Star of JBoss Community “Mr. Jaikiran Pai” has provided a fantastic demo and instructions in the following link on How to remotely invoke EJBs remotely which are deployed on JBoss AS7:

https://docs.jboss.org/author/display/AS71/EJB+invocations+from+a+remote+client+using+JNDI

In this example we will continue with what Jaikiran explained us in the above fantastic tutorial, Additionally we will also see different issues which we may face while invoking the EJBs remotely while using JBoss AS7 so that we can avoid some exceptions which we are going to discuss in this article below.

In this example we will mainly focus on following points

Point-1). What is the use of “jboss-ejb-client.properties” file at the client side classpath?
The above file need to be placed inside the clients classpath or inside a JAR which is present inside Client’s CLASSPATH.

Point-2). What will be the syntax of the JNDI name while invoking a Stateless or Stateful SessionBean?
For Stateless Beans JNDI Naming will be in following format:

ejb:<app-name>/<module-name>/<distinct-name>/<bean-name>!<fully-qualified-classname-of-the-remote-interface>

For Stateful Beans JNDI Naming will be in following format:

ejb:<app-name>/<module-name>/<distinct-name>/<bean-name>!<fully-qualified-classname-of-the-remote-interface>?stateful

Point-3). What all minimum set of Jars are required for a Remote Client in order to invoke an EJB deployed on JBoss AS7?
If you are using ANT scripts to run your client then you must make sure that we should have the following Jars at the client classpath along with the “jboss-ejb-client.properties” file :

        <path id="jboss.remote.ejb.classpath">
           <fileset dir="${jboss.module.dir}">
               <include name="org/jboss/ejb-client/main/jboss-ejb-client-1.0.0.Beta11.jar" />
               <include name="org/jboss/logging/main/jboss-logging-3.1.0.CR2.jar" />
               <include name="org/jboss/logmanager/main/jboss-logmanager-1.2.0.GA.jar" />
               <include name="org/apache/log4j/main/log4j-1.2.16.jar" />
               <include name="javax/ejb/api/main/jboss-ejb-api_3.1_spec-1.0.1.Final.jar" />
               <include name="org/jboss/marshalling/main/jboss-marshalling-1.3.4.GA.jar" />
               <include name="org/jboss/remoting3/main/jboss-remoting-3.2.0.CR6-darranl-1.jar" />
               <include name="org/jboss/xnio/main/xnio-api-3.0.0.CR5.jar" />
               <include name="org/jboss/sasl/main/jboss-sasl-1.0.0.Beta9.jar" />
               <include name="javax/transaction/api/main/jboss-transaction-api_1.1_spec-1.0.0.Final.jar" />
               <include name="org/jboss/xnio/nio/main/xnio-nio-3.0.0.CR5.jar" />
               <include name="org/jboss/sasl/main/jboss-sasl-1.0.0.Beta9.jar" />
               <include name="org/jboss/marshalling/river/main/jboss-marshalling-river-1.3.4.GA.jar" />
           </fileset>
        </path>

Developing Simple EJB3 Application

Point-1). Here we are using JBoss AS7.1 which can be downloaded from the following link: http://www.jboss.org/jbossas/downloads. I am using “jboss-as-7.1.0.CR1″ in this demo.

Point-2). Create a directory somewhere in your file system as “/NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1/standalone/EJB3_RemoteLookup” then create another directory “src” inside the “/NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1/standalone/EJB3_RemoteLookup”.

Point-3). Write the EJB Remote interface “CallerRemote.java” as following inside the directory “/NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1/standalone/EJB3_RemoteLookup/src”:

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

Point-3). Write the EJB Stateless Bean class “CallerBean.java” as following inside the directory “/NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1/standalone/EJB3_RemoteLookup/src”:

package remote;
import javax.ejb.*;
import javax.naming.*;
import java.util.*;

@Stateless
@Remote(CallerRemote.class)
public class CallerBean implements CallerRemote
 {
     public String testMethod(String name)
	    {
		   System.out.println("\n\n\t Bean's testMethod(String name) called....");
		   return "[CallerBean] returned Hello "+name;
	    }
 }

Point-4). As we are going to create an EAR file containing the above Stateless Session Bean so We will write the “application.xml” as following inside the directory “/NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1/standalone/EJB3_RemoteLookup/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>remoteEJB.jar</ejb>
  </module>
</application>

Developing EJB3 Remote Client

Point-5). Now we will write the Client side code to access the Above mentioned EJB Remotely so create a class “TestRemoteClient.java” as following inside the directory “/NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1/standalone/EJB3_RemoteLookup/src”:

package client;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Hashtable;
import remote.CallerRemote;

public class TestRemoteClient
  {
      public static void main(String ar[]) throws Exception
       {
         Hashtable<String,String> envs = new Hashtable<String,String>();
         envs.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
         InitialContext context = new InitialContext(envs);

         // Lookup Format will be For Stateless Session Beans
         // ejb:<app-name>/<module-name>/<distinct-name>/<bean-name>!<fully-qualified-classname-of-the-remote-interface>
         // For more info:  https://docs.jboss.org/author/display/AS71/EJB+invocations+from+a+remote+client+using+JNDI

         CallerRemote remote=(CallerRemote)context.lookup("ejb:TestRemoteEJBEAR/remoteEJB//CallerBean!remote.CallerRemote");
         System.out.println("\n\t remote.testMethod(\"MiddlewareMagic\") = "+remote.testMethod("MiddlewareMagic"));
       }
  }

Point-6). Now we will need to create a properties file “jboss-ejb-client.properties” as following inside the directory “/NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1/standalone/EJB3_RemoteLookup/src”:

remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false
remote.connections=default
remote.connection.default.host=localhost
remote.connection.default.port = 4447
remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false

NOTE: The above properties file need to be present inside the client’s classpath. In our case we are going to put this file inside a JAR file “remoteEJBClient.jar” and then we will add this JAR inside the client’s classpath.

Point-7). Now Start your JBoss Profile as following from inside the directory “/NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1/bin”:

.
 ./standalone.sh -c standalone-full.xml
.

Point-8). Now the most important part of building / deploying and running the application so in order to achieve that we will create a simple ant build script, So Create a “build.xml” file inside the “/NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1/standalone/EJB3_RemoteLookup” directory as following:

<project name="SingletonStartupService" default="all">
<property name="jboss.home" value="/NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1" />
<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="build" />
<property name="ear.name" value="TestRemoteEJBEAR.ear" />
<property name="ejb.jar" value="remoteEJB.jar" />
<property name="client.jar.name" value="remoteEJBClient.jar" />

        <path id="jboss.classpath">
           <fileset dir="${jboss.module.dir}">
               <include name="**/*.jar"/>
           </fileset>
        </path>

        <path id="jboss.remote.ejb.classpath">
           <fileset dir="${jboss.module.dir}">
               <include name="org/jboss/ejb-client/main/jboss-ejb-client-1.0.0.Beta11.jar" />
               <include name="org/jboss/logging/main/jboss-logging-3.1.0.CR2.jar" />
               <include name="org/jboss/logmanager/main/jboss-logmanager-1.2.0.GA.jar" />
               <include name="org/apache/log4j/main/log4j-1.2.16.jar" />
               <include name="javax/ejb/api/main/jboss-ejb-api_3.1_spec-1.0.1.Final.jar" />
               <include name="org/jboss/marshalling/main/jboss-marshalling-1.3.4.GA.jar" />
               <include name="org/jboss/remoting3/main/jboss-remoting-3.2.0.CR6-darranl-1.jar" />
               <include name="org/jboss/xnio/main/xnio-api-3.0.0.CR5.jar" />
               <include name="org/jboss/sasl/main/jboss-sasl-1.0.0.Beta9.jar" />
               <include name="javax/transaction/api/main/jboss-transaction-api_1.1_spec-1.0.0.Final.jar" />
               <include name="org/jboss/xnio/nio/main/xnio-nio-3.0.0.CR5.jar" />
               <include name="org/jboss/sasl/main/jboss-sasl-1.0.0.Beta9.jar" />
               <include name="org/jboss/marshalling/river/main/jboss-marshalling-river-1.3.4.GA.jar" />
           </fileset>
        </path>

        <target name="all" depends="deploy" />

        <target name="build_ear">
           <delete dir="${tmp.dir}" />
           <mkdir dir="${tmp.dir}" />
           <javac srcdir="${src.dir}" destdir="${tmp.dir}"  includes="*.java" classpathref="jboss.classpath"/>

           <jar jarfile="${tmp.dir}/${ejb.jar}" basedir="${tmp.dir}" compress="true" />

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

           <copy file="${tmp.dir}/${ear.name}" tofile="${output.dir}/${ear.name}"/>
           <delete file="${tmp.dir}/${ear.name}"/>
        </target>

        <target name="deploy" depends="build_ear">
            <echo message="*******************  Deploying the EAR file ${ear.name} *********************" />
            <echo message="********** ${output.dir}/${ear.name} to ${jboss.home}/standalone/deployments **********" />
            <copy todir="${jboss.home}/standalone/deployments/">
                <fileset dir="${output.dir}/">
                  <include name="${ear.name}"/>
                </fileset>
            </copy>
            <echo message="*******************  Deployed Successfully   *********************" />
        </target>

        <target name="run">
           <delete dir="${tmp.dir}" />
           <mkdir dir="${tmp.dir}" />
           <copy file="${src.dir}/jboss-ejb-client.properties" toDir="${tmp.dir}"/> 

           <javac srcdir="${src.dir}" destdir="${tmp.dir}"  includes="CallerRemote.java,TestRemoteClient.java" classpathref="jboss.classpath"/>
           <jar jarfile="${output.dir}/${client.jar.name}" basedir="${tmp.dir}" compress="true" />
           <delete dir="${tmp.dir}"/>

           <java classname="client.TestRemoteClient" fork="true">
               <classpath>
                  <pathelement location="${output.dir}/${client.jar.name}"/>
                  <path refid="jboss.remote.ejb.classpath"/>
               </classpath>
           </java>
        </target>        

</project>

NOTE: The only change in the above file you need to do is to change the “jboss.home” directory path in the second line of the above script to point to your own JBoss AS7 directory.

Step9). Now before running your ANT script to build and deploy the above webapplication you should have the ANT as well as JAVA set in the $PATH variable of the Shell / command prompt as following:

For Unix Based OS:
export PATH=/home/userone/jdk1.6.0_21/bin:/home/userone/org.apache.ant_1.6.5/bin:$PATH

For Windows Based OS:
set PATH=C:/jdk1.6.0_21/bin;C:/org.apache.ant_1.6.5/bin;%PATH%

Step10). Now once the PATH is set In the command/Shell prompt you can move inside the directory “/NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1/standalone/EJB3_RemoteLookup” and then run the ant to build and deploy the EJB based EAR applicationon your JBoss Standalone full profile, by running the command “ant deploy”

[userone@localhost EJB3_RemoteLookup]$ ant deploy
Buildfile: /NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1/standalone/EJB3_RemoteLookup/build.xml

build_ear:
    [mkdir] Created dir: /NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1/standalone/EJB3_RemoteLookup/tmp
    [javac] /NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1/standalone/EJB3_RemoteLookup/build.xml:42: warning: 'includeantruntime' was not set, defaulting to build.sysclasspath=last; set to false for repeatable builds
    [javac] Compiling 3 source files to /NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1/standalone/EJB3_RemoteLookup/tmp
      [jar] Building jar: /NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1/standalone/EJB3_RemoteLookup/tmp/remoteEJB.jar
    [mkdir] Created dir: /NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1/standalone/EJB3_RemoteLookup/tmp/META-INF
     [copy] Copying 1 file to /NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1/standalone/EJB3_RemoteLookup/tmp/META-INF
      [jar] Building jar: /NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1/standalone/EJB3_RemoteLookup/tmp/TestRemoteEJBEAR.ear
   [delete] Deleting: /NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1/standalone/EJB3_RemoteLookup/tmp/remoteEJB.jar
     [copy] Copying 1 file to /NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1/standalone/EJB3_RemoteLookup/build
   [delete] Deleting: /NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1/standalone/EJB3_RemoteLookup/tmp/TestRemoteEJBEAR.ear

deploy:
     [echo] *******************  Deploying the EAR file TestRemoteEJBEAR.ear *********************
     [echo] ********** build/TestRemoteEJBEAR.ear to /NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1/standalone/deployments **********
     [copy] Copying 1 file to /NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1/standalone/deployments
     [echo] *******************  Deployed Successfully   *********************

all:

BUILD SUCCESSFUL
Total time: 1 second

As soon as the EJB will be deployed on the JBoss AS7 you will see the following kind of output on your JBoss Console (STDOUT):

15:51:12,613 INFO  [org.jboss.as.server.deployment] (MSC service thread 1-8) Starting deployment of "TestRemoteEJBEAR.ear"
15:51:12,621 INFO  [org.jboss.as.server.deployment] (MSC service thread 1-4) Starting deployment of "remoteEJB.jar"
15:51:12,653 INFO  [org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor] (MSC service thread 1-4) JNDI bindings for session bean named CallerBean in deployment unit subdeployment "remoteEJB.jar" of deployment "TestRemoteEJBEAR.ear" are as follows:

	java:global/TestRemoteEJBEAR/remoteEJB/CallerBean!remote.CallerRemote
	java:app/remoteEJB/CallerBean!remote.CallerRemote
	java:module/CallerBean!remote.CallerRemote
	java:global/TestRemoteEJBEAR/remoteEJB/CallerBean
	java:app/remoteEJB/CallerBean
	java:module/CallerBean

15:51:12,687 INFO  [org.jboss.as.server] (DeploymentScanner-threads - 2) JBAS018565: Replaced deployment "TestRemoteEJBEAR.ear" with deployment "TestRemoteEJBEAR.ear"

Step11). Now we will try to compile and run the EJB3 remote Client application by running the command “ant run”

[userone@localhost EJB3_RemoteLookup]$ ant run
Buildfile: /NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1/standalone/EJB3_RemoteLookup/build.xml

run:
   [delete] Deleting directory /NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1/standalone/EJB3_RemoteLookup/tmp
    [mkdir] Created dir: /NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1/standalone/EJB3_RemoteLookup/tmp
     [copy] Copying 1 file to /NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1/standalone/EJB3_RemoteLookup/tmp
    [javac] /NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1/standalone/EJB3_RemoteLookup/build.xml:83: warning: 'includeantruntime' was not set, defaulting to build.sysclasspath=last; set to false for repeatable builds
    [javac] Compiling 2 source files to /NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1/standalone/EJB3_RemoteLookup/tmp
      [jar] Building jar: /NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1/standalone/EJB3_RemoteLookup/build/remoteEJBClient.jar
   [delete] Deleting directory /NotBackedUp/JBoss_All/jboss-as-7.1.0.CR1/standalone/EJB3_RemoteLookup/tmp
     [java] log4j:WARN No appenders could be found for logger (org.jboss.logging).
     [java] log4j:WARN Please initialize the log4j system properly.
     [java] log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
     [java]
     [java] 	 remote.testMethod("MiddlewareMagic") = [CallerBean] returned Hello MiddlewareMagic

BUILD SUCCESSFUL
Total time: 3 seconds

Some Common Issues which you might face

.
Issue-1). If you see the following Exception while invoking the remote EJB as following then you should double check your Clients CLASSPATH whether the “jboss-ejb-client.properties” file is present there or not in form of properties file or in form of a JAR file containing the “jboss-ejb-client.properties” file?

     [java] Exception in thread "main" java.lang.IllegalStateException: No EJB receiver available for handling [appName:TestRemoteEJBEAR,modulename:remoteEJB,distinctname:] combination
     [java] 	at org.jboss.ejb.client.EJBClientContext.requireEJBReceiver(EJBClientContext.java:344)
     [java] 	at org.jboss.ejb.client.EJBInvocationHandler.doInvoke(EJBInvocationHandler.java:92)
     [java] 	at org.jboss.ejb.client.EJBInvocationHandler.invoke(EJBInvocationHandler.java:83)
     [java] 	at $Proxy0.testMethod(Unknown Source)
     [java] 	at client.TestRemoteClient.main(Unknown Source)
     [java] Java Result: 1

BUILD SUCCESSFUL
Total time: 2 seconds

.
.
Issue-2). If you see the following Exception while invoking the remote EJB as following then you should double check your Clients CLASSPATH again.

     [java] Exception in thread "main" javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file:  java.naming.factory.initial
     [java] 	at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:645)
     [java] 	at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:288)
     [java] 	at javax.naming.InitialContext.getURLOrDefaultInitCtx(InitialContext.java:325)
     [java] 	at javax.naming.InitialContext.lookup(InitialContext.java:392)
     [java] 	at client.TestRemoteClient.main(Unknown Source)

The above Exception indicates that your Client Side classpath has missing the following set of JAR files if you are using ANT scripts then make sure that you include the following JARs using the classpathref:

        <path id="jboss.remote.ejb.classpath">
           <fileset dir="${jboss.module.dir}">
               <include name="org/jboss/ejb-client/main/jboss-ejb-client-1.0.0.Beta11.jar" />
               <include name="org/jboss/logging/main/jboss-logging-3.1.0.CR2.jar" />
               <include name="org/jboss/logmanager/main/jboss-logmanager-1.2.0.GA.jar" />
               <include name="org/apache/log4j/main/log4j-1.2.16.jar" />
               <include name="javax/ejb/api/main/jboss-ejb-api_3.1_spec-1.0.1.Final.jar" />
               <include name="org/jboss/marshalling/main/jboss-marshalling-1.3.4.GA.jar" />
               <include name="org/jboss/remoting3/main/jboss-remoting-3.2.0.CR6-darranl-1.jar" />
               <include name="org/jboss/xnio/main/xnio-api-3.0.0.CR5.jar" />
               <include name="org/jboss/sasl/main/jboss-sasl-1.0.0.Beta9.jar" />
               <include name="javax/transaction/api/main/jboss-transaction-api_1.1_spec-1.0.0.Final.jar" />
               <include name="org/jboss/xnio/nio/main/xnio-nio-3.0.0.CR5.jar" />
               <include name="org/jboss/sasl/main/jboss-sasl-1.0.0.Beta9.jar" />
               <include name="org/jboss/marshalling/river/main/jboss-marshalling-river-1.3.4.GA.jar" />
           </fileset>
        </path>

        <!-- Make Sure that the client classpath includes the above path reference/JARs as following -->

           <java classname="client.TestRemoteClient" fork="true">
               <classpath>
                  <pathelement location="${output.dir}/${client.jar.name}"/>
                  <path refid="jboss.remote.ejb.classpath"/>     <!--   IMPORTANT -->
               </classpath>
           </java>

.
.
Thanks
Middleware Magic Team