Temps de lecture : 5 min.

Historique

Lors de la sortie de la version 2.1 de Google Web Toolkit, une nouvelle API : RequestFactory a fait son apparition. Cette API a été conçue dans le but de simplifier l’interaction et la manipulation de données côté serveur depuis le client (javascript).
Auparavant, les développeurs utilisaient essentiellement GWT-RPC pour les appels de service impliquant des manipulations de données côté serveur.
Etant donné que le code côté client interagit avec du Java côté serveur, GWT-RPC fournit nativement
un mécanisme de sérialisation de données puissant. Cependant, cela ajoutait également un certain nombre de contraintes. Entres autres, le fait que tout objet échangé entre le client et le serveur devait être Serializable (au sens GWT), l’utilisation du pattern DTO qui induisait une redondance du code métier serveur au niveau du client etc.

Différence entre GWT-RPC et RequestFactory

La différence fondamentale entre les deux API réside dans l’orientation. GWT-RPC  est une API orientée service. A la base, elle permet d’invoquer des méthodes distantes d’une couche service pour interagir avec les données.

RequestFactory quant à elle est une API orientée donnée. Elle invoque directement des méthodes exposées non pas sur une couche service, mais sur les entités métiers (selon la terminologie GWT) elles-mêmes. C’est comme si on rendait les données serveurs disponibles au niveau du client. Les méthodes invoquées sont alors les méthodes des objets métiers.

Concepts de base

RequestFactory se base sur la notion de Proxy. Pour effectuer des opérations sur les données côté serveur, il définit un objet proxy qui “représente” cet objet serveur au niveau du client. Ce proxy est en fait une “interface” qui définit l’ensemble des méthodes qu’on voudrait exposer sur l’objet métier lui-même. Pour déclencher l’exécution d’une action sur l’objet côté serveur, il suffit de l’invoquer sur l’interface du proxy et RequestFactory se charge de l’envoi de la requête ainsi que de la sérialisation/déserialisation des données. Il est à noter qu’il utilise un protocole de sérialisation qui lui est propre et qui est différent de GWT-RPC.

Quelques règles à respecter

Le fonctionnement interne de RequestFactory impose quelques contraintes au niveau des classes métiers dont :

L’objet métier doit avoir un constructeur sans argument

la nécéssité d’avoir un champ VERSION dans la classe métier (annoté @Version en JPA). Ce champ permet à RequestFactory de tester si un objet a changé d’état.

Chaque Entité doit également disposer d’une méthode statique permettant de retrouver une instance de l’objet par son identifiant. Par exemple, pour l’entité de type Person, ce sera :

public static findPerson(Long id) {

// retrieving object by Id using EntityManager

}

Cas d’utilisation

Supposons alors qu’on ait un objet métier Personne dans notre application. Les objets de cette classe sont déstinés à être sauvegardés dans une base de données. On les appelle tout naturellement des “Entités” dans RequestFactory. On supposera que la persistance est gérée par Hibernate dans notre application et que tout a déjà été configuré (hibernate.cfg.xml, etc.). Nous avons donc dans notre domaine, le code suivant :

@Entity public class Person {
  @Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Version
@Column(name = "version")
private Integer version;

@Version
@Column(name = "name")
private String name;
@Column(name = "surname")
private String surname;

  @JoinColumn(name="address_id")
  Address address;

  public Personne() { }  // Getters and setters ...

}

Après avoir défini notre objet métier, on va donc lui adjoindre un “Proxy” qui sera une représentation de cet objet métier côté client. RequestFactory se charge alors de
propager les opérations invoquées sur les proxy vers les entités côté serveur. Il est également important de noter que RequestFactory envoie seulement les différences entre
ce qu’il y a côté serveur et côté client, économisant ainsi la bande passante.

Entity Proxy

Une entity proxy est un proxy qui comme son nom l’indique, représente une entité côté serveur. Pour la définir il suffit de créer une interface qui étend EntityProxy
et d’indiquer la classe métier qu’il représente via l’annotation @ProxyFor.

@ProxyFor(Person.class)
public interface PersonProxy extends EntityProxy {
public Person() { }
// Les méthodes exposées ...
public String getName();
public void setName(String name);
public AddressProxy getAddress();
public void setAddress(AddressProxy address);
// Les méthodes exposées ...
public String getName();
public void setName(String name);
public AddressProxy getAddress();
public void setAddress(AddressProxy address);
}

Les méthodes qu’on a défini dans le proxy sont les méthodes “invocables” sur l’objet métier. Ainsi, si on veut restreindre l’appel à certaines méthodes de l’objet métier
côté serveur, il suffit de ne pas l’indiquer dans le proxy. Ici par exemple, on a omis les modificateurs de Surname, ce qui veut dire qu’on ne pourra pas invoquer la méthode
de modification du prénom depuis le côté client.
Remarquons également que si une Entité A possède une référence vers une autre Entité B, les signatures du proxy AProxy feront référence à des BProxy (dans notre cas, AddressProxy).

Le lien entre le client et le serveur : RequestFactory

Après avoir défini les entités et leurs proxy, on crée maintenant la RequestFactory elle-même. C’est une interface qui comme son nom l’indique va créer les “stubs”
permettant la communication entre le client et le serveur.

public interface ApplicationRequestFactory extends RequestFactory

{

    PersonRequest personRequest();

    AddressRequest addressRequest();
}

PersonRequest et AddressRequest sont nos stubs pour les opérations concernant respectivement les entités Person et Address.

@Service(PersonServiceDAO.class)
public interface PersonneRequestContext extends RequestContext {

Request<List<PersonProxy>> getPersonList();

Request<PersonProxy> findPersonById(Long id);

}

Notons que les stubs utilisent toujours Request comme type de retour, où X est le paramètre de retour de la méthode de service.
Le stub doit nommer via une annotation, la méthode qui implémente les services côté serveur. Ainsi, dans notre exemple on a deux méthodes qu’on peut invoquer côté serveur
pour les entités de la classe Personne : on peut retrouver la liste complète ou encore retrouver une Personne par son identifiant.
On pourrait imaginer une classe service comme la suivante ( j’ai omis volontairement toute la partie de gestion des exceptions liées à la persistance des données pour une raison de lisibilité) :

public class PersonServiceDAO {

public List<PersonProxy> getPersonList()
  {
      List<Person> personList = new ArrayList();
      EntityManager entityManager = PersistenceManager.getEntityManagerFactory().createEntityManager();
      try {
          personList = entityManager.createQuery("FROM Person").getResultList();
      } catch (Exception e) {
          e.printStackTrace();
      }
      return personList;
  }

public Personne findPersonById(Long id) {
      EntityManager entityManager = PersistenceManager.getEntityManagerFactory().createEntityManager();
      Person person = null;
      try {
          person = entityManager.find(Person.class, id);
      } catch (Exception e) {
          e.printStackTrace();
      }
      return person;
  }
}

Utiliser RequestFactory

Enfin la dernière étape pour l’exécution de RequestFactory est de l’invoquer depuis le code client. Dans un premier temps, on doit initialiser le bus d’évènement. Une manière de procéder c’est de faire l’initialisation dans le constructeur de la classe dans laquelle on va utiliser RequestFactory.

public class OurClassWindow {
final EventBus eventBus = new SimpleEventBus();
ApplicationRequestFactory requestFactory = GWT.create(ApplicationRequestFactory.class);

public OurClassWindow() {
// Initialize event bus
requestFactory.initialize(eventBus);
}

}

Le mécanisme d’appels dans RequestFactory est très similaire à ce que l’on retrouve dans GWT-RPC. Après avoir retrouvé la requestContext, on invoque la méthode tout en
passant un callback qui sera appelé par RequestFactory au retour de la fonction.

Receiver<List<PersonProxy>> receiver = new Receiver<List<PersonProxy>>(){
@Override 	public void onSuccess(List<PersonProxy> response) {

// Do something with the response from RequestFactory

} };

requestFactory.personRequestContext.getPersonList().fire(receiver);

receiver ici, représente la méthode callback qui sera appelée au retour de la fonction. Comme dans GWT-RPC, il dispose de méthodes onSuccess et onFailure permettant de gérer
les cas de réussite ou d’échecs après l’appel.

Conclusion

Si vous avez déjà codé en GWT, vous savez à quel point il est fastidieux d’écrire des services uniquement pour ramener des données stockées en base  : RequestFactory est une manière élégante simple pour effectuer à moindre coût des appels vers un backend. L’API et la mise en oeuvre sont similaires à ceux de GWT-RPC, il est ainsi très facile de l’appréhender même si on est habitué à GWT-RPC.

pour aller plus loin : https://developers.google.com/web-toolkit/doc/latest/DevGuideRequestFactory