Migration d’une application vers Google App Engine

Cet article ouvre une série de billets ayant pour but de condenser et expliquer l'ensemble des difficultés que j'ai rencontrées lors du déploiement d'une application web standard vers GAE.

L'application initiale utilise quelques technologies classiques du web : Ajax (framework DWR), JSP, MySQL, Hibernate, JPA, Spring (AOP, MVC, DAO).

Configuration du projet

La plateforme GAE requiert l’ajout de dépendances vers :

  • API App Engine : pour l’accès aux fonctionnalités spécifiques (récupération de l’utilisateur connecté...)
  • Datanucleus (ORM) : le data store de App Engine étant du NoSQL, Hibernate n’est plus utilisable.
  • Objectify (recommandé) : couche d’abstraction de persistence.

Spring

GAE préconise plusieurs optimisations de la configuration Spring ainsi que certains usages spécifiques à sa plateforme, détaillés ci-dessous.

source : https://developers.google.com/appengine/articles/spring_optimization

L’application initiale est basée sur la détection automatique de beans typés par interfaces : la configuration du contexte ci-dessous permet à Spring de parcourir les packages indiqués pour trouver les implémentations à injecter :

<context:component-scan base-package="tudu.service"/>

Sur GAE, ce système est déconseillé (pour des raisons de performances de la plateforme) et il est recommandé de préférer un usage plus "simple" de l’injection, via une déclaration explicite des beans, par exemple :


<bean id="userService" class="tudu.service.impl.UserServiceImpl"/>
<bean id="tenantService" class="tudu.service.impl.TenantServiceImpl"/>
...

De la même façon, les dépendances doivent de préférence être entièrement configurées dans le fichier de contexte plutôt qu’automatiquement détectées par l’annotation @Autowired, par exemple:

Pour le lien entre un controller et un DAO du type :


public class MovieController {
  MovieDaoInterface movieDao;
  public void setMovieDao(MovieDaoInterface movieDao) {
    this.movieDao = movieDao;
  }
}

Il faut indiquer la configuration :


<bean class="com.example.controller.MovieController">
  <property name="movieDao" ref="movieDao"></property>
</bean>
<!-- movieDao is defined elsewhere in the container configuration -->
<bean id="movieDao" class="com.example.dao.MovieDao" />

De la même façon, l’injection de l’entityManager via l’annotation @PersistenceContext doit être remplacé par l’appel à une classe statique.

source : https://developers.google.com/appengine/docs/java/datastore/jpa/overview?hl=fr#Getting_an_EntityManager_Instance

SGBD

L’infrastructure de GAE utilise une base de donnée NoSQL Big Table développée en interne par Google, ce qui peut impliquer la refonte de toute la couche de persistence et d’accès à la BDD.

source : http://blog.xebia.fr/2009/07/16/la-persistance-dans-google-app-engine-partie-une-le-datastore/
source : https://developers.google.com/appengine/docs/whatisgoogleappengine?hl=fr

ORM et persistence

Le SGBD utilisé par GAE impose le remplacement de Hibernate/JPA (relationnel) par Datanucleus/Objectify.

source : http://blog.publysher.nl/2012/02/improving-appengine-performance-from.html
source : http://turbomanage.wordpress.com/2010/01/28/simplify-with-objectify/

Transactions

L’application initiale se base sur l’annotation @Transactional de Spring AOP pour l’injection des dépendances.

Ce mécanisme ne semble pas compatible avec GAE, qui compte quelques bugs à ce sujet.

source : https://groups.google.com/forum/?fromgroups#!topic/google-appengine-java/68qhETz-UFU
source : http://objectuser.wordpress.com/2009/06/30/spring-jdo-in-google-app-engine/

Il faut donc complètement revoir cet aspect et préférer la manipulation explicite des transactions, via Objectify ou plus directement comme par exemple :


DatastoreService datastore = DatastoreServiceFactory.getDatastoreService()
try {
    Transaction txn = datastore.beginTransaction();
    // do stuff
    txn.commit();
}
finally {
    if (txn.isActive()) {
        txn.rollback();
    }
}

source : http://stackoverflow.com/questions/5386144/two-different-methods-for-google-app-engine-datastore-transactions-which-to-u
source : https://developers.google.com/appengine/docs/java/datastore/transactions

Logs

Puisque GAE utilise un JRE modifié ne contenant plus les librairies java.io.*, il n’est plus possible de se reposer sur Log4j ou Logback. Il n’y a plus d’appender à définir pour conserver la trace des logs, ils seront seulement disponibles dans la console d’administration de l’application.

Dans l'article suivant, je détaillerai la configuration nécessaire du POM projet pour réaliser une application GAE.


Fichier(s) joint(s) :

0 commentaires: