1.3. Calling EJB3 beans with an RPC service

1.3.1. Defining the interface of the service

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.

1.3.1.1. Service interface

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]Note

GridData needs to be a serializable object. But the serialization is not the JDK serialization. This class needs to implements the com.google.gwt.user.client.rpc.IsSerializable interface.

The Exception (ServiceException) is also implementing the IsSerializable interface.

1.3.1.2. Asynchronous interface

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]Note

The ServiceException is not thrown by the solve() method of Async interface. The exceptions will be reported into the onFailure(Throwable t) method of the callback.

1.3.2. Classes used by the interfaces

1.3.2.1. The GridData class

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

1.3.2.2. The ServiceException exception

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;
    }

}

1.3.3. Implementation of the service

1.3.3.1. Implementation service class

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

}

1.3.3.2. Accessing to an EJB3 from this Remote Service.

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]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.

1.3.3.3. Calling facade bean from the remote service

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.

1.3.4. Calling Remote service from client side.

1.3.4.1. The gwt.xml file

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>

1.3.4.2. Calling service from client side.

1.3.4.2.1. Getting the service on client side

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");
1.3.4.2.2. Creating the callback

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) {
      ...
   }
};
1.3.4.2.3. Invoking the service

The invocation on the service is done by providing both arguments of the original method and the callback on the asynchronous interface.

serviceAsycn.solve(gridData, solvedCallback);