Copyright © 2006 ObjectWeb Consortium
This work is licensed under the Creative Commons Attribution-ShareAlike License. To view a copy of this license,visit http://creativecommons.org/licenses/by-sa/2.0/deed.en or send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
$Id: howtos.xml 898 2006-07-20 11:46:55Z benoitf $
Abstract
The HowTos provided in this guide allow the developer to discover and do some tasks with EasyBeans and some other components.
Table of Contents
List of Figures
Table of Contents
Abstract
The GWT (Google Web Toolkit) will be used as the framework for producing clients using AJAX technique while EJB3 will be on the server side in order to provide some services (like persistence).
EJB3 can ease the developers for the persistence part (with Java Persistence API) or for the business part.
The source of an application using EasyBeans and GWT can be browse on the Fisheye Sudoku EasyBeans site. The application is a Sudoku Game and this howto use some code of this application.
A JDK 5.0 is required to use EasyBeans.
![]() | Tip |
---|---|
GWT only support JDK 1.4 functions. So classes that will be transformed by GWT into JavaScript shouldn't use JDK 5 features (like annotations). Other classes can use the new features (like the EJB3). |
GWT can be used without Eclipse but in this HowTo, Eclipse is used.
This HowTo is done by using the EasyBeans Tomcat package. Note that it works with the JOnAS package too.
EasyBeans can be downloaded from EasyBeans Web Site.
Tomcat 5.5.17 was used. It should work with other versions. Download from Tomcat site.
Toolkit can be downloaded from the Google Web Toolkit download page.
GWT comes with an eclipse project creator tool. The project will have the name Sudoku and will be created in the $HOME/workspace directory.
$ cd gwt-linux-1.0.21 $ ./projectCreator -eclipse Sudoku -out $HOME/workspace/sudoku Created directory $HOME/workspace/sudoku/src Created file $HOME/workspace/sudoku/.project Created file $HOME/workspace/sudoku/.classpath
A GWT application needs to be added to the eclipse project. The name of the class to generate is given to the application creator tool.
Some files will be generated :
The Sudoku.gwt.xml
file which describes
the module with entry point. This file will be also used to add
the mapping for the remote service using EasyBeans.
The Sudoku.html
file which is a
skeleton page which call the widget's examples.
The Sudoku.java
file which is the class
responsible to load widgets (which implements the
EntryPoint
interface).
Then, there are 3 scripts that allow to launch or compile files. Note that compile script will be replaced by an ant task in this HowTo.
$ ./applicationCreator -out $HOME/workspace/sudoku -eclipse -out org.objectweb.easybeans.demo.sudoku.web.client.Sudoku Created directory $HOME/workspace/sudoku/src/org/objectweb/easybeans/demo/sudoku/web Created directory $HOME/workspace/sudoku/src/org/objectweb/easybeans/demo/sudoku/web/client Created directory $HOME/workspace/sudoku/src/org/objectweb/easybeans/demo/sudoku/web/public Created file $HOME/workspace/sudoku/src/org/objectweb/easybeans/demo/sudoku/web/Sudoku.gwt.xml Created file $HOME//workspace/sudoku/src/org/objectweb/easybeans/demo/sudoku/web/public/Sudoku.html Created file $HOME//workspace/sudoku/src/org/objectweb/easybeans/demo/sudoku/web/client/Sudoku.java Created file $HOME/workspace/sudoku/Sudoku.launch Created file $HOME/workspace/sudoku/Sudoku-shell Created file $HOME/workspace/sudoku/Sudoku-compile
The two interface of the service need to be present in the client package. In this howto, the package name is org.objectweb.easybeans.demo.sudoku.web.client.service;
Two interfaces have to be defined :
The first one is the interface that will be implemented by the remote service.
The other interface is the asynchronous interface. It is
always a void method, a new parameter needs to be added on the
method and the name of the interface is suffixed by
Async
.
Here is the example of a service that takes an argument and return an object (A solver).
package org.objectweb.easybeans.demo.sudoku.web.client.service; import org.objectweb.easybeans.demo.sudoku.web.client.api.GridData; import org.objectweb.easybeans.demo.sudoku.web.client.api.ServiceException; import com.google.gwt.user.client.rpc.RemoteService; /** * Interface used to call the servlet facade and then EJB3. * @author Florent Benoit */ public interface ServletFacadeService extends RemoteService { /** * Solve a sudoku grid and send in return the solved grid. * @param gridData the grid to solve * @return the solved grid. * @throws ServiceException if solving fails. */ GridData solve(GridData gridData) throws ServiceException; }
![]() | Note |
---|---|
GridData needs to be a serializable object. But the
serialization is not the JDK serialization. This class needs to
implements the
The Exception (ServiceException) is also implementing the
|
The signature is almost the same except the following :
The return type is void.
An extra argument is added in the method signature : the
AsyncCallback
object.
The name of the class is ending with
Async
keyword.
package org.objectweb.easybeans.demo.sudoku.web.client.service; import org.objectweb.easybeans.demo.sudoku.web.client.api.GridData; import com.google.gwt.user.client.rpc.AsyncCallback; /** * Asynchronous interface. Same parameters of interface but all methods have an * AsyncCallback parameter and are void !. * @author Florent Benoit */ public interface ServletFacadeServiceAsync { /** * Asynchronous call to the solve method. * @param gridData the grid to solve * @param callback the callback to use for this method */ void solve(GridData gridData, AsyncCallback callback); }
![]() | Note |
---|---|
The ServiceException is not thrown by the solve() method of
Async interface. The exceptions will be reported into the
|
This class implements
IsSerializable
interface.
package org.objectweb.easybeans.demo.sudoku.web.client.api; import com.google.gwt.user.client.rpc.IsSerializable; /** * Represents the data of a sudoku grid. * It is a serializable object (gwt) used by remote service. * @author Florent Benoit */ public class GridData implements IsSerializable { ... }
This class implements
IsSerializable
interface and it stores
the message.
Also an empty constructor is required for the serialization.
package org.objectweb.easybeans.demo.sudoku.web.client.api; import com.google.gwt.user.client.rpc.IsSerializable; /** * Exception thrown by the remote service. <br /> * Exception needs to be serializable (gwt) * @author Florent Benoit */ public class ServiceException extends Exception implements IsSerializable { /** * The message of the exception. */ private String msg; /** * Empty message. */ public ServiceException() { super(); } /** * Builds an exception with a given message. * @param msg the message of the exception. */ public ServiceException(final String msg) { super(msg); this.msg = msg; } /** * Gets the message of the exception. * @return the message of the exception. */ public String getMessage() { return this.msg; } }
The implementation of the service will run on the server side. Then it doesn't need to be present in the client package (No JavaScript transformation). The class will be in the server package.
The service is provided as a servlet. Then, the class needs to
extend the
com.google.gwt.user.server.rpc.RemoteServiceServlet
class.
Class will looks like :
package org.objectweb.easybeans.demo.sudoku.web.server.service; import org.objectweb.easybeans.demo.sudoku.web.client.api.GridData; import org.objectweb.easybeans.demo.sudoku.web.client.api.ServiceException; import com.google.gwt.user.server.rpc.RemoteServiceServlet; /** * Implementation of the service that runs on the server side. <br /> * All is delegate to the EJB3 session facade. * @author Florent Benoit */ public class ServletFacadeServiceImpl extends RemoteServiceServlet implements ServletFacadeService { /** * Solve a sudoku grid and send in return the solved grid. * @param gridData the grid to solve * @return the solved grid. * @throws ServiceException if solving fails. */ public GridData solve(final GridData gridData) throws ServiceException { ... } }
A sessionBean facade will be used for delegating all the requests.
The service will use a getFacade() method. InitialContext is built then the facade is searched and the session bean is returned. The bean could be cached to avoid to get a new bean each time.
Here is the code of this method :
/** * Gets the session facade bean. * @return the session bean. * @throws Exception if facade is not retrieved */ private SudokuFacade getFacade() throws Exception { SudokuFacade sudokuFacade = null; Context initialContext = null; Hashtable<String, String> env = new Hashtable<String, String>(); env.put(Context.INITIAL_CONTEXT_FACTORY, "org.objectweb.carol.jndi.spi.MultiOrbInitialContextFactory"); initialContext = new InitialContext(env); sudokuFacade = (SudokuFacade) initialContext.lookup("SudokuFacade"); return sudokuFacade; }
![]() | Note |
---|---|
The JNDI name is very simple as the attribute mappedName was used on the @Stateless annotation of the EJB3. No Provider URL is used as the service and the EJB3s are running with the same registry. Default URL will be ok. |
When an operation needs to be done on the facade session bean, it is done by using the following way :
try { ... = getFacade().methodName(); } catch (Exception e) { throw new ServiceException(e.getMessage()); }
The exception thrown are wrapped in ServiceException. Only the message is kept and thrown to the client. Error could be printed in the error log to have the full trace on the server side.
The service needs to be added in an xml file in order to declare
it. In this howto, the file is named
Sudoku.gwt.xml
.
The servet element is added in this file with the class implementing the service and the endpoint of this service (/facade is the following example).
<module> <!-- Inherit the core Web Toolkit stuff. --> <inherits name='com.google.gwt.user.User'/> <!-- Specify the app entry point class. --> <entry-point class='org.objectweb.easybeans.demo.sudoku.web.client.Sudoku'/> <servlet path='/facade' class='org.objectweb.easybeans.demo.sudoku.web.server.service.ServletFacadeServiceImpl'/> </module>
An endpoint needs to be created. The path of the endpoint will use the name of the module and then /facade (the same entry than in the gwt.xml file)
Endpoint will be something like "http://localhost:8080/org.objectweb.easybeans.demo/facade".
By using GWT.getModuleBaseURL(), the port and the host don't need to be known.
ServletFacadeServiceAsync servletFacadeServiceAsync = (ServletFacadeServiceAsync) GWT .create(ServletFacadeService.class); ServiceDefTarget endpoint = (ServiceDefTarget) servletFacadeServiceAsync; endpoint.setServiceEntryPoint(GWT.getModuleBaseURL() + "/facade");
Before invoking the service, a callback needs to be built. The interface of the service that is used is the asynchronous interface. As this interface requires a callback, it needs to be constructed before.
A callback provides two methods :
An onSuccess(final Object
result)
method if the call completes with
success.
An onFailure(final Throwable caught)
method if call fails.
The result object in the case of the resolve method can be casted in GridData as it is the return type of the original method's interface.
final AsyncCallback solvedCallback = new AsyncCallback() { /** * Called when an asynchronous call completes successfully. It is * always safe to downcast the parameter (of type * <code>Object</code>) to the return type of the original method * for which this is a callback. */ public void onSuccess(final Object result) { GridData solvedGridData = (GridData) result; ... } /** * Called when an asynchronous call fails to complete normally. * @param caught the failure. */ public void onFailure(final Throwable caught) { ... } };
A web.xml
file has to be created to defines a
servlet for the remote service that was defined and the mapping. The
mapping has to be the same that was used in the client when searching
the endpoint to use.
Here is an example of the xml file :
<?xml version="1.0" encoding="ISO-8859-1"> <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4"> <servlet> <servlet-name>ServletFacadeService</servlet-name> <servlet-class>org.objectweb.easybeans.demo.sudoku.web.server.service.ServletFacadeServiceImpl</servlet-class> </servlet> <servlet-mapping> <servlet-name>ServletFacadeService</servlet-name> <url-pattern>/facade</url-pattern> </servlet-mapping> </web-app>
GWT provides some shell script to generate the client's side. It's better to use ant task.
First, a classpath needs to be defined. This classpath should include GWT libraries and the path to the source code of the Entry point :
<path id="gwt.classpath"> <pathelement location="${project.dir}/src" /> <pathelement location="${gwt.sdk.location}/gwt-user.jar" /> <pathelement location="${gwt.sdk.location}/gwt-dev-windows.jar" /> </path>
Then, code can be compiled :
<target name="compile" description="Compile demo" depends="init"> <javac srcdir="${src.dir}" destdir="${classes.dir}" debug="on"> <classpath refid="base.classpath" /> </javac> </target>
And at the end, GWT generation can be done :
<target name="generate-gwt" depends="compile"> <java classname="com.google.gwt.dev.GWTCompiler" fork="true"> <arg value="-out" /> <arg value="${dist.www.dir}" /> <arg value="org.objectweb.easybeans.demo.sudoku.web.Sudoku" /> <classpath refid="base.classpath" /> </java> </target>
The AJAX client will be bundled in a war file and it will contains the GWT runtime library.
The war task of ant can be used to do the package :
The output generated by the GWT compiler is added at the root of
the WAR file, the libraries go in the WEB-INF/lib
folder while all classes go in
WEB-INF/classes
folder.
<target name="build.war" description="Build war file" depends="compile, generate-gwt, removejavax"> <war jarfile="${dist.webapps.dir}/sudoku.war" webxml="${resources.dir}/web.xml"> <fileset dir="${dist.www.dir}/org.objectweb.easybeans.demo.sudoku.web.Sudoku" /> <lib dir="${tmp.dir}"> <include name="gwt-user.jar" /> </lib> <classes dir="${classes.dir}"> <include name="**/**" /> </classes> </war> </target>
![]() | Warning |
---|---|
The gwt-user.jar provided by Google contains javax.servlet.* classes and then it won't be deployed on Tomcat container. These classes have to be removed. An ant target is doing this job : <target name="removejavax" depends="init"> <mkdir dir="${tmp.dir}/classes" /> <unjar src="${lib.dir}/gwt-user.jar" dest="${tmp.dir}/classes"> <patternset> <exclude name="javax/**" /> </patternset> </unjar> <jar jarfile="${tmp.dir}/gwt-user.jar"> <fileset dir="${tmp.dir}/classes" /> </jar> </target> |
The war file generated by Ant should be copied into CATALINA_HOME/webapps
folder while the EJB3
implementation should be copied into CATALINA_HOME/ejb3s
folder.
Documentation on how to start Tomcat/EasyBeans can be found in the documentation section of EasyBeans web site.
The GWT toolkit create a .launch script allowing to launch an embedded browser and an embedded web container allowing to debug applications within Eclipse. This script has to be modified for EasyBeans access.
The following lines have been added :
<listEntry value="<?xml version="1.0" encoding="UTF-8"?> <runtimeClasspathEntry externalArchive="/easybeans-sudoku/lib/ow_carol.jar" path="3" type="2"/> "/> <listEntry value="<?xml version="1.0" encoding="UTF-8"?> <runtimeClasspathEntry externalArchive="/easybeans-sudoku/lib/ow_carol_cmi.jar" path="3" type="2"/> "/> <listEntry value="<?xml version="1.0" encoding="UTF-8"?> <runtimeClasspathEntry externalArchive="/easybeans-sudoku/lib/ow_easybeans_api.jar" path="3" type="2"/> "/> <listEntry value="<?xml version="1.0" encoding="UTF-8"?> <runtimeClasspathEntry externalArchive="/easybeans-sudoku/lib/ow_easybeans_core.jar" path="3" type="2"/> "/> <listEntry value="<?xml version="1.0" encoding="UTF-8"?> <runtimeClasspathEntry externalArchive="/easybeans-sudoku/lib/ejb-2_1-api.jar" path="3" type="2"/> "/> <listEntry value="<?xml version="1.0" encoding="UTF-8"?> <runtimeClasspathEntry externalArchive="/easybeans-sudoku/lib/ow_ejb3_core_api.jar" path="3" type="2"/> "/> <listEntry value="<?xml version="1.0" encoding="UTF-8"?> <runtimeClasspathEntry externalArchive="/easybeans-sudoku/lib/ow_ejb3_persistence_api.jar" path="3" type="2"/> "/>
These libraries are used to access to the EasyBeans services.
This HowTo can be completed if some comments or questions are not answered by this guide. The idea was to illustrate how to call remote EJB3s running on EasyBeans by using Google Web Toolkit.
For developping EJB3 components, the user guide of EasyBeans can be used.