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) { ... } };