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("nnt 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("nt 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 log4j:WARN No appenders could be found for logger (org.jboss.logging). log4j:WARN Please initialize the log4j system properly. log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info. 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?
Exception in thread "main" java.lang.IllegalStateException: No EJB receiver available for handling [appName:TestRemoteEJBEAR,modulename:remoteEJB,distinctname:] combination at org.jboss.ejb.client.EJBClientContext.requireEJBReceiver(EJBClientContext.java:344) at org.jboss.ejb.client.EJBInvocationHandler.doInvoke(EJBInvocationHandler.java:92) at org.jboss.ejb.client.EJBInvocationHandler.invoke(EJBInvocationHandler.java:83) at $Proxy0.testMethod(Unknown Source) at client.TestRemoteClient.main(Unknown Source) 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.
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 at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:645) at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:288) at javax.naming.InitialContext.getURLOrDefaultInitCtx(InitialContext.java:325) at javax.naming.InitialContext.lookup(InitialContext.java:392) 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
August 20th, 2012 on 9:14 pm
Thanks for the list of the required jars. Getting them is always a sticking point for me when I’m minimizing the size of my remote client.
One small point: You listed a jar twice:
11
And then this question: how did you come up with this list? Was there an automated process?
Thanks,
Josh Britton
August 20th, 2012 on 9:29 pm
Hmmm: not sure why my post didn’t show the name of the doubly listed jar. It’s the ‘sasl’ jar.
Further note:
I also had to add javassist:
org/javassist/main/javassist-3.15.0-GA.jar
August 26th, 2012 on 11:47 pm
Hi Pinkushn,
The article was written for “jboss-as-7.1.0.CR1”, But Starting JBoss AS 7.1.0.Final, a jboss-client jar is shipped in the distribution. It’s available at “$JBOSS_HOME/bin/client/jboss-client-7.1.0.Final.jar”. Place this jar in the classpath of your client application. So now you only need to place this single JAR on the client side.
Thanks
Jay SenSharma