11/27/2007

A simple EJB 3 on JBoss application server

This is a simple tutorial on how to run EJB 3 app on Jboss application server. The version I'm using is JBossAS-5.0.0 beta2. The following app works with JBoss AS 5 & 6. AS 7 introduced significant changes to remote JNDI, and I will write a new post(Standalone Java Client for JBoss AS 7.1.1) to cover it.

Note that JBossAS-5.0.0 beta2 does not work with JDK 6 (see my previous post). So you will need to set environment variable JAVA_HOME to a JDK 5 location (e.g., set JAVA_HOME=C:\jdk5) for JBoss server. This also requires us to compile EJB classes with JDK 5 for the class file to be compatible with server JVM. However, it is perfectly OK to use JDK 6 for the client.

1. project directory structure:

C:\simple-ejb3> tree /A /F
+---classes
| \---foo
\---src
\---foo
Client.java
FooBean.java
FooRemote.java
2. create java src files under src\foo:
FooRemote.java:

package foo;
import javax.ejb.*;

@Remote
public interface FooRemote {
public String echo(String s);
}
FooBean.java:

package foo;
import javax.ejb.*;

@Stateless
public class FooBean implements FooRemote {
public String echo(String s) {
return s;
}
}
Client.java

package foo;
import javax.ejb.*;
import javax.naming.*;

public class Client {
public static void main(String[] args) throws Exception {
//JBoss' default remote jndi: <ejb-name>/remote
final String jndiName = "FooBean/remote";
Context ic = new InitialContext();
System.out.println("about to look up jndi name " + jndiName);
Object obj = ic.lookup(jndiName);
System.out.println("lookup returned " + obj);

FooRemote foo = (FooRemote) obj;
String s = foo.echo("Hello Foo on JBoss!");
System.out.println(foo + " echo returned " + s);
}
}
3. Compile java src. An environment variable JBOSS_HOME is set for convenience but it's not required.
C:\simple-ejb3\classes> set JBOSS_HOME=C:\jboss
C:\simple-ejb3\classes> javac -d . -classpath %JBOSS_HOME%\client\jbossall-client.jar;. ..\src\foo\*.java
4. start JBoss default config:
set JAVA_HOME=C:\jdk5
C:\simple-ejb3\classes> %JBOSS_HOME%\bin\run.bat
5. package and autodeploy ejb-jar. We can combine the 2 steps in 1 by using %JBOSS_HOME%\server\default\deploy as the destdir:
C:\simple-ejb3\classes>
jar cvf %JBOSS_HOME%\server\default\deploy\foo-ejb.jar
foo\FooBean.class foo\FooRemote.class
6. Prepare jndi.properties and log4j.properties somewhere in client classpath, e.g., simple-ejb3\classes directory:

# jndi.properties:
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
java.naming.provider.url=localhost:1099

# Set root category priority to INFO and its only appender to CONSOLE.
log4j.rootCategory=INFO, CONSOLE

# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Threshold=INFO
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=- %m%n
7. run the standalone java client. Note that I use JDK 6 on the client to use the wildcard (*) in classpath. JBoss client needs a sleuth of jars scattered in several JBoss server directories. With * in classpath I don't need to figure out which jars to include.
C:\simple-ejb3\classes> java -cp %JBOSS_HOME%\lib\*;%JBOSS_HOME%\client\*;%JBOSS_HOME%\server\default\lib\*;. foo.Client

about to look up jndi name FooBean/remote
lookup returned jboss.j2ee:jar=foo-ejb.jar,name=FooBean,service=EJB3
jboss.j2ee:jar=foo-ejb.jar,name=FooBean,service=EJB3 echo returned Hello Foo on JBoss!
If you have to run client with JDK 5, this is the detailed classpath:
%JBOSS_HOME%\lib\jboss-aop-jdk50.jar;
%JBOSS_HOME%\client\jbossall-client.jar;
%JBOSS_HOME%\server\default\lib\jbosssx.jar;.
This is the classpath necessary to run this simple client. Other more complex client may need additional jars. And who knows what will change in the next JBoss release? So it's always a good idea to use the wildcard classpath with JDK 6.

8. undeploy the ejb module:
del %JBOSS_HOME%\server\default\deploy\foo-ejb.jar
Tags: , ,

42 comments:

coroner said...

Thank you very much. It means a lot to get all this help from a total stranger.

coroner.john@gmail.com

Anonymous said...

Thanks a helluva lot man! This was the best plain vanilla tutorial I could find on google. Else, everything is with eclipse, glassfish ..... Best clear and concise tutorial. Thanks again!!

Zammetti said...

Hello Sir,

It is the simplest tutorial that i drilled out in the internet .I am desperately looking for EJB3 to run each of the time it shows NameNotFoundExcption .So,please could you tell me that how can i overcome this problem ??
Also i have a doubt that how our client stick with Bean class (As we did perform some lookup action in JNDI with EJB 2.1)

So,please help me !!

I would highly obliged !!

javahowto said...

EJB lookup should work with EJB 3, EJB 2, from standalone java client, webapp, swing client, etc. Your NameNotFoundException may be caused by incorrect jndi name used in the lookup.

I have some posts on NameNotFouncException, for example, http://javahowto.blogspot.com/2006/06/fix-namenotfoundexception-incorrect.html

These posts are based on GlassFish, but should also apply to JBoss.

seenu said...

Thank you very much. Each and every line is so clear, anyone who wants to start with ejb can learn easily and in efficient manner.
Thanks a lot buddy.

allen said...

ill been looking for an ejb3 tutorial till i found this... this is great! it works.. thanks a lot

Saurabh said...

Really superb, very very simple and a perfect plug and play :-)
Thanks a lot

Anonymous said...

thank you

thota said...

Thanks for a really simple example. When I run client I'm getting the below error, can you please help me out.

Thota said...

Sorry I forgot to add the error in my previous comment.
Caused by: javax.naming.CommunicationException: Failed to retrieve stub from server localhost/127.0.0.1:8080 [Root exception is java.io.EOFException]

Anonymous said...

hai,
I could not understand the project structure u have mentioned.
I tried like this,
created a folder "simple-ejb3"
simple-ejb3
|-src
|-foo
|-Client.java
|-FooBean.java
|-FooRemote.java
I am getting a error lie this

C:\simple-ejb3\src\foo->set JBOSS_HOME=c:\jboss

C:\simple-ejb3\src\foo->javac -d . -classpath %JBOSS_HOME%\client\jbossall-client
.jar;. ..\src\foo\*.java
javac: file not found: ..\src\foo\*.java
Usage: javac options source files
use -help for a list of possible options

Help me to run this program pls..

beata said...

hai,
I could not understand the project structure u have mentioned.
I tried like this,
created a folder "simple-ejb3"
simple-ejb3
|-src
|-foo
|-Client.java
|-FooBean.java
|-FooRemote.java
I am getting a error lie this

C:\simple-ejb3\src\foo->set JBOSS_HOME=c:\jboss

C:\simple-ejb3\src\foo->javac -d . -classpath %JBOSS_HOME%\client\jbossall-client
.jar;. ..\src\foo\*.java
javac: file not found: ..\src\foo\*.java
Usage: javac options source files
use -help for a list of possible options

Help me to run this program pls..

javahowto said...

Your current directory when running javac is src/foo.

The DOS prompt in my example shows it's in classes directory.

You need to cd into classes directory, or change the classpath and src files accordingly.

beata said...

c:\simple_ejb3\classes\src\foo:javac -d . -classpath %JBOSS_HOME%\client\jbosall-client.jar;. ..\src\foo\*.java
This is what i tried..
I am getting file not found error..
I am a beginner pls help me to get rid of this...

javahowto said...

Check your directory layout. This is wrong:

c:\simple_ejb3\classes\src\foo

The above directory doesn't seem right.

You should have:
c:\simple_ejb3\classes\foo (where compiled class files reside)

c:\simple_ejb3\src\foo (where java source files reside)

When you run javac, make sure you are at c:\simple_ejb3\classes.

beata said...

C:\simple-ejb3\classes>set JBOSS_HOME=D:\jboss-5.0.0.CR2

C:\simple-ejb3\classes> javac -d . -classpath %JBOSS_HOME%\client\jbossall-clien
t.jar;. ..\src\foo\*.java
..\src\foo\Client.java:8: ';' expected
final String jndiName = "FooBean/remote"
^
1 error


I am getting this error when compiled

javahowto said...

The sample code in the post was missing a semi-colon:

final String jndiName = "FooBean/remote"

Fixed. Thanks for catching that.

beata said...

C:\>cd simple-ejb3

C:\simple-ejb3>cd classes

C:\simple-ejb3\classes>set JBOSS_HOME=D:\jboss-5.0.0.CR2

C:\simple-ejb3\classes> javac -d . -classpath %JBOSS_HOME%\client\jbossall-clien
t.jar;. ..\src\foo\*.java
..\src\foo\FooRemote.java:5: class FooBean is public, should be declared in a fi
le named FooBean.java
public class FooBean implements FooRemote {
^
..\src\foo\FooBean.java:5: cannot find symbol
symbol: class FooRemote
public class FooBean implements FooRemote {
^
..\src\foo\Client.java:14: cannot find symbol
symbol : class FooRemote
location: class foo.Client
FooRemote foo = (FooRemote) obj;
^
..\src\foo\Client.java:14: cannot find symbol
symbol : class FooRemote
location: class foo.Client
FooRemote foo = (FooRemote) obj;
^
4 errors

when given like this,
final String jndiName = "FooBean/remote";
I am getting the above error...

javahowto said...

One of your java source file has the wrong name. FooRemote.java should really be named FooBean.java.

I'll add the file name for each source file to the post to make it clearer.

beata said...

C:\>cd simple-ejb3

C:\simple-ejb3>cd classes

C:\simple-ejb3\classes>set JBOSS_HOME=D:\jboss-5.0.0.CR2

C:\simple-ejb3\classes> javac -d . -classpath %JBOSS_HOME%\client\jbossall-clien
t.jar;. ..\src\foo\*.java
..\src\foo\FooRemote.java:5: class FooBean is public, should be declared in a fi
le named FooBean.java
public class FooBean implements FooRemote {
^
..\src\foo\FooBean.java:5: cannot find symbol
symbol: class FooRemote
public class FooBean implements FooRemote {
^
..\src\foo\Client.java:14: cannot find symbol
symbol : class FooRemote
location: class foo.Client
FooRemote foo = (FooRemote) obj;
^
..\src\foo\Client.java:14: cannot find symbol
symbol : class FooRemote
location: class foo.Client
FooRemote foo = (FooRemote) obj;
^
4 errors

I am getting these errors while compiling.

Sougata said...

Hi,
The ejb deploys fine. But I'm unable to run the client. I'm using jboss5.0.0.GA. The exception is as follows:-

Exception in thread "main" javax.naming.NamingException: Could not dereference object [Root exception is java.
lang.reflect.UndeclaredThrowableException]

Can you help me on this...

Regards,
Sougata

Anonymous said...

I got this exception:

cannot find symbol
@Stateless
@Remote
2 errors

how can i fix it?

thank you

javahowto said...

javax.ejb.Stateless and javax.ejb.Remote should be in jbossall-client.jar. Can you check the version of your jboss AS (make sure if supports ejb 3). These ejb api are new in ejb 3.

Anonymous said...

Thanks! It works as is. I tried it with jdk5 against jboss 5.1.

The only change in invoking the client was

java -cp /Library/jbossAS/Home/lib/jboss-aop-jboss5.jar:/Library/jbossAS/Home/client/jbossall-client.jar:. foo.Client

Anonymous said...

Excellent EJB program follow the steps as it is and it works great
For more help mail to harshals@synechron.com

Manu said...

Could you please tell how to create a client program which is running on a different computer other than where the jboss is running?

javahowto said...

To access ejb deployed on a different machine, the code and steps should be the same. You will need to modify jndi.properties in your client classpath, especially:

java.naming.provider.url=localhost:1099

There is no standard way for a java application to access EJB in an appserver. Every appserver does it in slightly different way.

***Sam*** said...

Thanku very much... Its helped me more

Anonymous said...

Very good tutorial.Just followed the same steps and it worked. I have tried it in a linux plaform, so only changes made is with the path setting using export instead of set.

Anver said...

If the ejb Client is running in a different machine using JBOSS App Server you might get javax.naming.CommunicationException

To resolve this issue start your JBOSS Server with -b option

Change in Step 4
4. start JBoss default config:
C:\simple-ejb3\classes>
%JBOSS_HOME%\bin\
run.bat -b yourIPAddress

Change in Step 6
jndi.properties:
java.naming.provider.url=jnp://yourIPAddress:1099

Note: Check the Port 1099 is used by the JBOSS server. To check
go to the ->jmx-console -> "service=Naming" -> search for "JNP service"

Anonymous said...

Hi Manu :
use the command
Run.bat -b 0.0.0.0 to start jboss from command line.
modify the following line
java.naming.provider.url=:1099
copy the client and remote class file and jndi.properties and log4j.properties in the m/c from where you will run the client.

Anonymous said...

Please provide solution of below when running client progam :-
C:\simple-ejb3\classes>java -cp C:\DevProjects\jboss-4.2.3.GA\jboss-4.2.3.GA\lib\*;C:\DevProjects\jboss-4.2.3.GA\jbo
.2.3.GA\client\*;C:\DevProjects\jboss-4.2.3.GA\jboss-4.2.3.GA\server\default\lib\*;. foo.client
about to look up jndi name FooBean/remote
Exception in thread "main" javax.naming.NameNotFoundException: FooBean not bound
at org.jnp.server.NamingServer.getBinding(NamingServer.java:529)
at org.jnp.server.NamingServer.getBinding(NamingServer.java:537)
at org.jnp.server.NamingServer.getObject(NamingServer.java:543)
at org.jnp.server.NamingServer.lookup(NamingServer.java:267)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:305)
at sun.rmi.transport.Transport$1.run(Transport.java:159)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:155)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:535)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:790)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:649)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:662)
at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:255)
at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:233)
at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:142)
at org.jnp.server.NamingServer_Stub.lookup(Unknown Source)
at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:667)
at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:627)
at javax.naming.InitialContext.lookup(InitialContext.java:392)
at foo.client.main(client.java:15)

Thanks in advance .
Regards,
MAM

Anonymous said...

Thank You.

Anonymous said...

Good one to start with.
Thanks

G. Karuppiah Das

animeworld said...

Hi. Could you provide an update for JBoss As 7? Thank you.

javahowto said...

Standalone java client and remote JNDI is not yet supported in JBoss AS 7. I will post a new example once it's implemented.

Anonymous said...

I have a curious problem with JBoss AS 6.1: if I have a JSF Managed Bean in a WAR file, and a Remote EJB in its own EJB-JAR file, @EJB doesn't work.

But if I package the same WAR and same EJB-JAR inside an EAR the @EJB annotation do work.

javahowto said...

I suspect your @EJB is missing some information that is needed by JBoss to resolve this ejb-ref. How can JBoss find the target bean of this ejb-ref? It's typically done by a mapping in appserver-specific descriptor file.

When packaging both the ejb module and web module together in EAR, JBoss can infer the target of the ejb-ref.

Anonymous said...

Thank you for the really simple EJB3 demo.

It run well with my JBAS55.1.0GA on JDK1.6.x.

However I got a warning such as this:
EJBTHREE-1246: Do not use InterceptorsF
actory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container

Anonymous said...

The WARN: message

"WARN [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container"

is resolved. I found this stackoverflow link very helpful
http://stackoverflow.com/questions/491007/jboss-what-does-the-warning-ejbthree-1246-from-the-interceptorregistry-mean.

Commenting out the following 2 lines from ejb3-interceptors-aop.xml made the trick.

Mamta Sharan said...

Waoo!!
This code really help & specially help in comments abt. errors :D
Thanks all :)

Anonymous said...

thanks to author. i runned on jdk7 :
jndi and log4j properties files inside src folder.and foo folder too in src folder.in foo folder there are 3 classes compiled . open command line and change to src folder and make commands as described. one more time thanks to mister Author.

p.s. Sir may you plan to make jms tutorial too ,thanks advance