1.3. Calling EJB3 Beans with an RPC Service

1.3.1. Defining the Interface of the Service

The two interfaces of the service must be present in the client package. In this howto, the package name is org.objectweb.easybeans.demo.sudoku.web.client.service.

The following two interfaces must be defined:

  • The interface that will be implemented by the remote service

  • The asynchronous interface, which is always a void method. A new parameter must be added on the method and the name of the interface must have a suffix of Async.

1.3.1.1. Service Interface

The following is an example of a service that takes an argument and returns 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;

    /**
     * Checks if a sudoku grid is valid.
     * @param gridData the grid to check
     * @throws ServiceException if checking fails.
     */
    void check(GridData gridData) throws ServiceException;


    /**
     * Generate a sudoku grid and send it in return.
     * @return the generated grid.
     * @throws ServiceException if solving fails.
     */
    GridData generate() throws ServiceException;

}
[Note] Note

GridData must be a serializable object. However, this serialization is not JDK serialization. This class must implement the com.google.gwt.user.client.rpc.IsSerializable interface.

The Exception (ServiceException) also implements the IsSerializable interface.

1.3.1.2. Asynchronous Interface

The signature is almost the same, except for the following:

  • The return type is void.

  • An extra argument is added in the method signature: the AsyncCallback object.

  • The name of the class ends with the 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);

    /**
     * Checks if a sudoku grid is valid.
     * @param gridData the grid to check
     * @param callback the callback to use for this method
     */
    void check(GridData gridData, AsyncCallback callback);


    /**
     * Generates a sudoku grid and send it in return.
     * @param callback the callback to use for this method
     */
    void generate(AsyncCallback callback);
}
[Note] Note

The ServiceException is not thrown by the solve() method of the Async interface. The exceptions will be reported in 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 the 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 the IsSerializable interface and stores the message.

Additionally, 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. Implementing the Service

1.3.3.1. Implementation Service Class

The implementation of the service will run on the server side. Therefore, it need not 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 must extend the com.google.gwt.user.server.rpc.RemoteServiceServlet class.

The Class will look like the following:

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 {

    /**
     * Checks if a sudoku grid is valid.
     * @param gridData the grid to check
     * @throws ServiceException if checking fails.
     */
    public void check(final GridData gridData) throws ServiceException {
        try {
            getFacade().check(gridData.getData());
        } catch (Exception e) {
            throw new ServiceException(e.getMessage());
        }
    }

    /**
     * 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 {
        int[][] solvedData = null;
        try {
            solvedData = getFacade().solve(gridData.getData());
        } catch (Exception e) {
            throw new ServiceException(e.getMessage());
        }
        GridData solvedGridData = new GridData();
        solvedGridData.setData(solvedData);
        return solvedGridData;
    }

...

}

1.3.3.2. Accessing 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 can be cached to avoid getting a new bean each time.

The following is the code for 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>();
        // Use the smart factory
        env.put(Context.INITIAL_CONTEXT_FACTORY, SmartContextFactory.class.getName());
        // Use the default Provider URL port
        env.put(Context.PROVIDER_URL, "smart://localhost:2503");
        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. The InitialContext factory is the smart factory. This factory allow to download the classes when they are required. The PROVIDER_URL is the default URL of the Smart component.

1.3.3.3. Calling the Facade Bean from the Remote Service

When an operation must be performed on the facade session bean, it is done in the following way:

   try {
     ... = getFacade().methodName();
    } catch (Exception e) {
       throw new ServiceException(e.getMessage());
    }

The exception thrown is wrapped in the ServiceException. Only the message is kept and thrown to the client. The error can be printed to the error log to have a full trace on the server side.

1.3.4. Calling the Remote Service from the Client Side

1.3.4.1. The gwt.xml File

The service must be added to an xml file in order to declare it. In this howto, the file is named Sudoku.gwt.xml.

The servet element is added to this file along with the class implementing the service and the endpoint of the 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. Callingathe Service from the Client Side

1.3.4.2.1. Getting the Service on the Client Side

An endpoint must be created. The path of the endpoint will use the name of the module and then /facade (the same entry as in the gwt.xml file).

The endpoint will be something similar to "http://localhost:8080/org.objectweb.easybeans.demo/facade".

By using GWT.getModuleBaseURL(), the port and the host need not 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 must be built. The asynchronous interface of the service is used. As this interface requires a callback, it must be constructed beforehand.

A callback provides two methods:

  • An onSuccess(final Object result) method if the call completes successfully.

  • An onFailure(final Throwable caught) method if the call fails.

For the onSuccess method, the result parameter can be cast to GridData, which 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 service can be invoked by providing the arguments of the original method, one of which is the callback on the asynchronous interface.

serviceAsycn.solve(gridData, solvedCallback);