Monday, March 29, 2010

Walkthrough: Integrating GWT with JBoss 5.1.0 & EJB3

This tutorial describes steps to easily create a JavaEE enterprise application, using the following components:

  • Google Web Toolkit (version 1.7 or 2.0)
  • Eclipse Ganymede or Galileo, Java EE version
  • GWT Eclipse Plugin
  • JBoss AS 5.1.0.GA
  • JBoss Tools plugin
Hint: Anything that is a user action will be in green.

Getting Started


If you haven’t already done so, you need to download all of those components. See ‘links’ at the end of this document for help. This tutorial requires the JavaEE version of Eclipse.

  • Unpack the JBoss distribution to wherever you would like.
  • Unpack/Install Eclipse, and start it up
  • Install the GWT Plugin for Eclipse, using the link provided for instructions
  • Install the JBoss Tools plugin for Eclipse, using the link provided.
Once you have all those components downloaded and installed, create a new empty workspace in Eclipse, and close the welcome screen.

1. Create the Server Environment


In eclipse, Open Window/Preferences and select Server/Runtime Environments
Click Add.


Select JBoss Community/JBoss 5.1 Runtime, and click Next.
(You won’t see this option unless you have installed the JBoss Tools Eclipse Plugin.)



Click Browse to configure the JBoss Home Directory. (This is wherever you unpacked it in Getting Started)
Select a server configuration; ‘default’ is fine for now.

Click Finish
Click OK to close the preferences.

2. Create an Enterprise Application Project


Select File/New/Enterprise Application Project
Enter the project name (I chose ‘tutorial_ear’ for this example.)



Click Finish

This just created an Enterprise Application aRchive Project, which will be used to jar up all the other components for easy deployment.

3. Create an EJB Project



Select File/New/EJB Project
Enter the project name (I chose tut_ejb here)
Check the Add project to an EAR setting



Click Next
Click Next again
At this point, you can choose if you want to have an EJB Client Jar or not. I left this checked, just 'cuz it's the default.

4. Create an EJB


Right click the EJB project, and select New/Session Bean(EJB3.x)
Set the Java Package and the Class name.
For this tutorial, we will only create a local interface.



Click Finish

In the ejbClient project, open the corresponding bean interface (SampleBeanLocal), and add a method. In this case, I added a method called getEJBHello(String name).



Save the file.

Go to the implementation (SampleBean.java), and implement the method:



Save the file.

5. Create the GWT Project

Click on the Google New Web Application Project icon in the toolbar
Set the project name, the package, and then configure the SDK.
Uncheck Use Google App Engine, as we’re using JBoss instead.



Click Finish.
At this point, your workspace configuration should look like this.



If you did not select the ejbClient project, then of course that project will not be here.

5.1 Modify the GWT project

Some things need to be done to the GWT project right off, before doing anything else.

5.1.1 Add Facets to the project

Open the Navigator view (Window/Show View/Navigator), open the gwt project, and double-click the .project file to open it.
Find the natures list, and add the following three items to it:

<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
<nature>org.eclipse.wst.jsdt.core.jsNature</nature>


(The result is it should look like this:)


Close the .project file
Close the gwt project, by right-clicking it, and selecting Close Project.
Reopen it, by right-clicking and select Open Project.
(This will cause eclipse to accept the new natures.)

5.1.2 Configure Project Facets

Right click the project, and select Properties.
Select Project Facets.
Enable Java
Enable Dynamic Web Module



BEFORE DOING ANYTHING ELSE, Click on Further configuration available...
Click Next to get to the Web Module configuration
Change the Content directory value to “war” so it aligns with the GWT generated project.



Click OK to close the Further Configuration
Click OK to close the project facets

Note: If you click OK before changing the Content directory, eclipse will irreversibly set the content directory; you cannot click "further configuration" and change it later. (Although, you can edit its .project file!)

5.1.3 Update the web.xml

In order for EJB injection to work, we need to update the web.xml web-app version from 2.3 to 2.5.
Find the gwt app’s web.xml, and open it.
Replace the header (including the web-app node), with the folllowing:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">


Save it.
If you plan on using JNDI lookups, and not injection, then you can skip this step. For example, if you need to use Stateful beans, then you probably don’t want to use injection. (See dev tips at the end.)

5.2 Add the GWT project to the EAR

Right click the EAR project, and select Properties.
Select Java EE Module Dependecies, and check the GWT project.



Click OK

6. Call the EJB!

Open the GreetingServiceImpl class using the greatest Eclipse tool of all time: Open Type.
Click the Open Type toolbar button, or type Shift-Ctrl-T. (I like this shortcut so much, I am thinking of mapping it to the spacebar!)
Inject the EJB. In this case, my bean interface is called SampleBeanLocal.
So, to inject it, I add the code:

@EJB
SampleBeanLocal sampleBean;




At this point, the project needs to have it’s build path updated...
Hover over the red squigglies on SampleBeanLocal, and select Fix Project Setup.
That should take care of that.

Modify the GWT server code to use the EJB:



7. Compile the GWT application

Click the GWT Compiler icon, and compile your app.
Refresh the ‘war’ folder on your GWT application, so eclipse will see the changes. You can do this by right-clicking the project, and selecting refresh.

We’re almost done!!!

8. Configure the JBoss Server

Click on the Servers tab near the bottom. If you don’t see it, you can select Window/Show View/Servers.
Right click in the servers view, and select New/Server
Select JBoss AS 5.1



Click Finish
This depends on Step 1, Configuring the JBoss Runtime Environment. If you did not complete that step, then you can click on Configure runtime environments here, to set things up now.

Right click the new server, and select Add and Remove.



Add the EAR to the Configured side, and click Finished

9. Start the server!

Right-click the server, and select either Start or Debug. Debug will let you set breakpoints in your server-side code.

After a while, in the console, you should hopefully see that your apps have deployed:

12:34:26,195 INFO [EJBContainer] STARTED EJB: org.example.tutorial.ejb.SampleBean ejbName: SampleBean

12:34:26,215 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI:

tutorial_ear/SampleBean/local - EJB3.x Default Local Business Interface
tutorial_ear/SampleBean/local-org.example.tutorial.ejb.SampleBeanLocal - EJB3.x Local Business Interface

12:34:26,292 INFO [TomcatDeployment] deploy, ctxPath=/tut_gwtapp

12:34:26,119 INFO [JBossASKernel] Added bean(jboss.j2ee:ear=tutorial_ear.ear,jar=tut_ejb.jar,name=SampleBean,service=EJB3) to KernelDeployment of: tut_ejb.jar

12:34:26,122 INFO [EJB3EndpointDeployer] Deploy AbstractBeanMetaData@1a98d7b{name=jboss.j2ee:ear=tutorial_ear.ear,jar=tut_ejb.jar,name=SampleBean,service=EJB3_endpoint bean=org.jboss.ejb3.endpoint.deployers.impl.EndpointImpl properties=[container] constructor=null autowireCandidate=true}

12:34:26,194 INFO [SessionSpecContainer] Starting


Once it is started, you can open your browser and go to:
http://localhost:8080/tut_gwtapp
and you should see your GWT application come up!




(Note there is a missing space after the Hello. Ooops. That's a bug in the SessionBean.)


10. Source code


You can download the source files from Here. Note that I deleted the gwt-user.jar from the tut_gwtapp to reduce the size. Namely, that is file tut_gwtapp/war/WEB-INF/lib/gwt-user.jar. It is part of the standard GWT distibution.


11. Conclusion

Using these awesome, powerful tools, you can integrate these components easily, to quicky build enterprise applications.

  • Why would I want to use EJBs?
EJB3 comes with some very powerful features! I will let you know as soon as I think of any. Okay, here’s one: Many existing EA code bases are already using EJBs as their primary way of doing business logic, and they are well understood by Java architects.

Something to watch out for, however, is that a Stateful EJB session bean may not be what you think it is. You might consider using @EJB to inject a stateful bean into your servlet code. This will likely lead to problems, however, as this bean is in no way tied to the web session, and so every call to your servlet will get a new bean.
Instead, when you need a stateful bean, you will need to look up the bean as per normal, and then cache it somewhere, eg., in the http session.

One downside to using Session Beans from your code is that you can't call them directly from the client code. You must make normal GWT RPC calls, and in the server code, make the call to the EJB... Haven’t figured out a way around that yet, and I don't think there is one.

12. Dev Cycle Tips

  • GWT compilation
The GWT compiler writes to the war folder. After compiling, you need to refresh that folder, so that Eclipse knows about the changes, and will redeploy the resources.

  • JBoss Startup time
It takes a long time to start and stop JBoss. So, don’t do that!
Start the server once.
If you are running in debug mode, you typically only need to refresh your war directory, and your changes will be deployed immediately, for client code.
For server code changes, you typically only need to save the file, and it will be deployed immediately, while in debug mode.
If your server changes do require a restart, don’t restart JBoss, but rather, undeploy and then redeploy the EAR. This is lots faster.

  • Export
Once you are happy with your EAR project, you can export it using File/Export...

13. Links

20 comments:

Unknown said...

Hi, great tutorial,

So we have to compile the gwt project just once right?
Also one note, have a look at the last GEP it is no longer mandatory to point to the war folder.
And this structure should work with maven right?

Regards.

JS said...

Hi @bot, thanks!

You should only have to compile the GWT project once, but you will need to recompile it any time the client code changes, and then refresh the war folder in eclipse.
What is GEP?
Unfortunately, I have not worked with maven.

I am planning a follow-up post that describes an ant build file to build the EAR, including doing the GWT compile.

- Jamie

Unknown said...

Hi,
GEP is google eclipse plugin.
Well, isn't the whole point to use "development mode", not to recompile the gwt project every time?

Unknown said...

Hi,

I am getting NullPointerException even after modifying web.xml as per given instruction in 5.1.3; It not injecting EJB at client end :( I am using JBoss AS 5.1, JDK 1.6_17 on WindowsXP sp2.

JS said...

Dr. Shraddha,

I would recommend attempting to access the EJB using JNDI to start with.

Does the server startup logs indicate that your EJB was properly deployed?

CWAN said...

Hi, I have used your source files and the JBoss server starts fine but whn I click the "Send" button it errors with:
07:59:44,880 ERROR [[greetServlet]] Allocate exception for servlet greetServlet
java.lang.Error: Unresolved compilation problems:
The import javax.ejb cannot be resolved
EJB cannot be resolved to a type
The method getEJBHello(String) from the type SampleBeanLocal refers to the missing type String

at org.example.tutorial.gwtapp.server.GreetingServiceImpl.(GreetingServiceImpl.java:3)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)

Any clues? My guess is that it is trying to find the jboss-j2ee.jar in which the javax.ejb classes exist. However, this jar is not on my JBoss server (jboss-5.0.1.GA). It all compiles OK in the Eclipse environment but not when accessed on the actual server.

JS said...

Hi CWAN,

I am afraid I have not used JBoss AS 5.0.1-GA, only 5.1.0. Can you download 5.1.0-GA and try that?
Also, I assume you used the 5.0 server adaptor in step 8.
Something looks really wrong, if it can't find String:
"SampleBeanLocal refers to the missing type String"

Jamie.

Fritz said...

Hi Jamie,

I followed all the steps but in part 9. Jboss cant seem to find the EAR.
copied and pasted the last part
=========
16:14:28,812 INFO [EJB3EndpointDeployer] Deploy AbstractBeanMetaData@17515f7{name=jboss.j2ee:jar=profileservice-secured.jar,name=SecureProfileService,service=EJB3_endpoint bean=org.jboss.ejb3.endpoint.deployers.impl.EndpointImpl properties=[container] constructor=null autowireCandidate=true}
16:14:28,812 INFO [EJB3EndpointDeployer] Deploy AbstractBeanMetaData@496daa{name=jboss.j2ee:jar=profileservice-secured.jar,name=SecureDeploymentManager,service=EJB3_endpoint bean=org.jboss.ejb3.endpoint.deployers.impl.EndpointImpl properties=[container] constructor=null autowireCandidate=true}
16:14:28,812 INFO [EJB3EndpointDeployer] Deploy AbstractBeanMetaData@15391e0{name=jboss.j2ee:jar=profileservice-secured.jar,name=SecureManagementView,service=EJB3_endpoint bean=org.jboss.ejb3.endpoint.deployers.impl.EndpointImpl properties=[container] constructor=null autowireCandidate=true}
16:14:28,875 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=profileservice-secured.jar,name=SecureDeploymentManager,service=EJB3
16:14:28,890 INFO [EJBContainer] STARTED EJB: org.jboss.profileservice.ejb.SecureDeploymentManager ejbName: SecureDeploymentManager
16:14:28,921 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI:

SecureDeploymentManager/remote - EJB3.x Default Remote Business Interface
SecureDeploymentManager/remote-org.jboss.deployers.spi.management.deploy.DeploymentManager - EJB3.x Remote Business Interface

16:14:28,984 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=profileservice-secured.jar,name=SecureManagementView,service=EJB3
16:14:28,984 INFO [EJBContainer] STARTED EJB: org.jboss.profileservice.ejb.SecureManagementView ejbName: SecureManagementView
16:14:28,984 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI:

SecureManagementView/remote - EJB3.x Default Remote Business Interface
SecureManagementView/remote-org.jboss.deployers.spi.management.ManagementView - EJB3.x Remote Business Interface

16:14:29,031 INFO [SessionSpecContainer] Starting jboss.j2ee:jar=profileservice-secured.jar,name=SecureProfileService,service=EJB3
16:14:29,031 INFO [EJBContainer] STARTED EJB: org.jboss.profileservice.ejb.SecureProfileServiceBean ejbName: SecureProfileService
16:14:29,031 INFO [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI:

SecureProfileService/remote - EJB3.x Default Remote Business Interface
SecureProfileService/remote-org.jboss.profileservice.spi.ProfileService - EJB3.x Remote Business Interface

16:14:29,187 INFO [TomcatDeployment] deploy, ctxPath=/admin-console
16:14:29,250 INFO [config] Initializing Mojarra (1.2_12-b01-FCS) for context '/admin-console'
16:14:31,203 INFO [TomcatDeployment] deploy, ctxPath=/
16:14:31,234 INFO [TomcatDeployment] deploy, ctxPath=/jmx-console
16:14:31,328 INFO [Http11Protocol] Starting Coyote HTTP/1.1 on http-localhost%2F127.0.0.1-8080
16:14:31,343 INFO [AjpProtocol] Starting Coyote AJP/1.3 on ajp-localhost%2F127.0.0.1-8009
16:14:31,343 INFO [ServerImpl] JBoss (Microcontainer) [5.1.0.GA (build: SVNTag=JBoss_5_1_0_GA date=200905221053)] Started in 29s:203ms

==================

I can access http://localhost:8080/ but the server cannot find http://localhost:8080/tut_gwtapp. any idea on this? i have been working on this problem for about a week..

thanks

JS said...

Fritz,

With the JBoss server running, look in the 'Servers' window. Expand the jboss server, and right-click 'tutorial_ear', and select 'Remove'.

That should cause some activity in the JBoss console output.

Then, right-click the server (while it's still running), select "Add and Remove...", and add tutorial_ear back in again.
Again, this should cause more activity in the console output window.

An alternative is to right-click on the tutorial_ear project, select "Export...", and export it to an ear file. Then, manually copy the ear file into the JBoss deploy folder, and see if that works.

Jamie.

agata said...

Hello,
Your tutorial helped me a lot!:)
Now I must find the way how to send my ejb entities from server to client side... which probably won't be so easy since gwt client has his own libriaries and dependencies world:)

Fritz said...

Hi Jamie,

Think the problem was with the 5.2 Add the GWT project to the EAR wherein i am supposed to use Java EE Module dependencies. Since im using helios. I went to deployment assembly option and selected 'project'. Is this the correct thing to do? I followed every step except that one.

JS said...

Hi Fritz,

I am downloading Helios now... I have not used it yet. Here's what I got from Google for Helios Eclipse:

"Helios Eclipse is a comic by a Malaysian female cartoonist, Kaoru"

Anyway, I'll see what happens with Helios shortly.

JS said...

Hi Fritz,

I hope you got it working...

Yes, Helios no longer has the Java EE Module Dependencies property.

You are right. What I did:
-Selected the EAR properties
-Selected Deployment Assembly
-Clicked Add...
-Selected Project / Next
-Selected tut_gwtapp / Finish
- OK to close the properties.

At this point, I was able to deploy the EAR, and everything came up normally using Helios.

beto said...

Hello,
Your tutorial helped me a lot too but i have a question, i don't know if you can help me. I started the server in debug mode but i can't able to make debugger works in the gwt part. Have you got any solution?

sorry for my english ;)

Unknown said...

First of all, great tutorial!! I have been looking for it like a month, thanks a lot!!
But I have a problem...I m using Helios and a newer GWT version at deployment time during the server started It throws an exception with this trace:

12:29:21,114 INFO [TomcatDeployment] deploy, ctxPath=/GWTTutorial
12:29:21,205 WARN [WebEJBRemoteHandler] EJBTHREE-1289: Using legacy EjbEncInjector, because mappedName for enc "env/vista.server.GreetingServiceImpl/sampleBean", field "null" is null (container.environmentRefGroup.annotatedEjbReferences = [AnnotatedEJBReferenceMetaData{name=vista.server.GreetingServiceImpl/sampleBean,ejb-ref-type=null,link=null,ignore-dependecy=false,mapped/jndi-name=null,resolved-jndi-name=null,beanInterface=class ejbs.SampleEjb}])
12:29:21,224 ERROR [TomcatDeployment] ENC setup failed
java.lang.IllegalStateException: Resolution should not happen via injection container
at org.jboss.web.tomcat.service.TomcatInjectionContainer.getEjbJndiName(TomcatInjectionContainer.java:640)
at org.jboss.injection.EjbEncInjector.inject(EjbEncInjector.java:80)
at org.jboss.web.tomcat.service.TomcatInjectionContainer.populateEnc(TomcatInjectionContainer.java:482)
at org.jboss.web.tomcat.service.deployers.TomcatDeployment$EncListener.lifecycleEvent(TomcatDeployment.java:471)
at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:117)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4388)
at org.jboss.web.tomcat.service.deployers.TomcatDeployment.performDeployInternal(TomcatDeployment.java:310)
at org.jboss.web.tomcat.service.deployers.TomcatDeployment.performDeploy(TomcatDeployment.java:142)
at org.jboss.web.deployers.AbstractWarDeployment.start(AbstractWarDeployment.java:461)
at org.jboss.web.deployers.WebModule.startModule(WebModule.java:118)
at org.jboss.web.deployers.WebModule.start(WebModule.java:97)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.jboss.mx.interceptor.ReflectedDispatcher.invoke(ReflectedDispatcher.java:157)
at org.jboss.mx.server.Invocation.dispatch(Invocation.java:96)
at org.jboss.mx.server.Invocation.invoke(Invocation.java:88)
at org.jboss.mx.server.AbstractMBeanInvoker.invoke(AbstractMBeanInvoker.java:264)
at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:668)
at org.jboss.system.microcontainer.ServiceProxy.invoke(ServiceProxy.java:206)
at $Proxy38.start(Unknown Source)

from the trace I ve been trying to fix this but I cant really get the the problem:

java.lang.IllegalStateException: Resolution should not happen via injection container
at org.jboss.web.tomca....
..

Do you have any idea?
Thanks Again

Nino said...

Jamie thnx for the great tutorial :)
I do have a problem though, with ejb injection to the web service.
It doesn't happen, so in runtime it blows up with NullPointerException on call to ejb's fuction.

I have used jbossall-client.jar
from client folder in jboss-5.1.0.GA distribution to resolve classpath for EJB annotation, and it doesn't work, and your tutorial is a little bit vague on what you used to make it work.


other people seem to be having the same problem with JBoss 5.1 GA:
http://community.jboss.org/message/340280

Could you please help with this?

JS said...

Nino,
Did you update your web.xml to be 2.5 instead of 2.3?

JS said...

Hi beto,

Unfortunately, this does not support client-side debugging from Eclipse.

To debug client-side stuff, I use Firefox/Firebug. Compiling using the 'Pretty' mode makes it readable, anyway.

Firebug has some pretty nice javascript tools.

JS said...

Hi Satiro,

I don't know what went wrong there. I will be downloading the 2.1 release of GWT and trying it out shortly, to see if I get the same problem.

Unknown said...

Hello. Really great post. Everything works as expected.

You talked about creating an ANT script to build and deploy all this.

Did you make any progress on this ?

Thank you very much for sharing such valuable information with the community.