Google App Engine Java : du JEE en trompe l'oeil...

Juillet 27th, 2009

Après mes précédents essais narrés ici, vient l'heure des premières conclusions :

Le cloud, ce n'est pas du local
Sans revenir sur les détails techniques, finalement bassement matériels et assez volatiles (nul doute que la plupart seront corrigés d'ici une version ou deux), un élément clé d'un développement Google App Engine m'est apparu lors de ce petit exercice : l'environnement local reproduit finalement assez mal le comportement de votre application (GWT) sur les serveurs Google !
En effet, le serveur Jetty embarqué pour le hosted mode GWT reste un moteur de servlet classique, dont le comportement est relativement bien connu.
Ce n'est hélas pas le cas de GAE, dont la gestion de certaines ressources diffère sensiblement. Idem pour la persistance.
Du coup, mon premier conseil est de déployer aussi souvent que possible : ce n'est pas parce que votre appli marche en local qu'il en sera de sur les serveurs Google !

JEE dry
Du coup, je serais tenté de penser que GAE est une sorte de JEE-dry : ça utilise les API de JEE, ça se programme comme du JEE, ça se teste comme du JEE, mais au final, ce n'est pas du JEE !
Pire, nombres d'habitudes JEE prises risquent de vous emmener droit dans le mur!
Alors que Google tendent de nous faire croire que l'on peut programmer une application GAE comme du JEE classique, mes expériences récentes tendent à me faire penser le contraire.

In fine, la manière efficace de programmer en Java avec GAE sera sans doute au contraire d'oublier un peu tous ces réflexes acquis sur nos serveurs d'applications, apprendre de nouvelles API plus en phase avec la gestion des ressources du cloud computing (BigTable, authentification, etc...).

A ce tire, je vous conseille grandement la lecture de l'article de Xebia sur le datastore BigTable: entre les lignes, on comprend bien pourquoi utiliser la persistance à base de JPA est sans doute une erreur, tant le modèle sous-jacent est différent d'une SGBDR classique (c'est ce que l'un de mes client actuel appelle "Faire rentrer un carré dans un rond").
Il n'y a qu'à voir le nombre de posts sur le forum de GAE/J concernant les difficultés d'implémentation de la couche de persistance à base de JPA ou de JDO pour se rendre compte que l'utilisation de l'API "bas niveau" semble l'option la plus sage...

GWT : un ajout de dernière minute ?
Souvenez-vous, la dernière fois, j'étais assez stupéfait qu'il soit impossible d'envoyer des entités persistantes vers la couche de présentation GWT.
Après investigation, j'ai découvert que le process de sérialisation GWT n'a pas évolué d'un iota depuis la version 1.5 il y a plus d'un an !
En passant, notez que la situation a désormais évoluer et que GWT 2.0 devrait supporter les entités JDO de DataNucleus (la correction est dans le trunk).

Histoire d'accréditer un peu plus cette thèse, la gestion des ressources GWT (Javascript, HTML, CSS) en dehors de tout filtre programmable rend encore plus compliqué le développement d'une vraie application sur le cloud de Google. Autant dire que si la convergence est en cours, elle n'est pas encore là, loin s'en faut !

GAE + GWT + Spring Security = une équation compliquée !

Juillet 27th, 2009

J'avoue, je ne sais pas ce qu'il m'a pris le jour où j'ai décider de sécuriser une application Google App Engine/GWT avec Spring Security. Une envie de nuit blanches, d'interrogations métaphysique et géométrie impossible sans doute... ou tout simplement le bon vieux piège du "je connais, ça va marcher".

Dans mon esprit, cela semblait lumineux : une solution robuste (Spring Security, maintes fois utilisées) pour une application interne business relativement sensible et dont je n'avais pas envie d'assurer l'hébergement.

Pour une fois, je vais essayer de limiter la technique à la portion congrue, les réflexions sur GAE (plus importante que les détails bassement informatiques) seront développées dans le billet suivant.

Et dire qu'en local, ça marche !

Première constatation de taille : ajouter Spring Security à une application GWT/GAE ne présente aucune difficulté. Depuis que GWT 1.6 utilise une arborescence standard, l'utilisation de la librairie Spring est vraiment aisée.

Lancement en local : nickel
Déploiement sur le cloud : La page d'accueil GWT s'affiche d'entrée, en lieu et place de la page de login |-|.
Début des ennuis !

1. Recompilation needed
Un petit coup d'oeil à la console permet de cerner l'origine du problème.
Comme indiqué ici, il manque une classe au support JRE de Google App Engine, utilisée par Spring Security : java.lang.String$CaseInsensitiveComparator
En attendant une correction de part ou d'autre, la seule solution est de recompiler Spring Security en modifiant proprement la classe AbstractAuthenticationToken. Pour ma part, j'ai codé mon propre CaseInsenstiveComparator, basé sur la classe Collator (supportée elle par GAE/J) :


public class StringInsensitiveComparator implements Serializable, Comparator {
private transient Collator col;

public StringInsensitiveComparator() {}

public int compare(Object strA, Object strB ) {
if (col == null)
{
col = Collator.getInstance();
col.setStrength(Collator.PRIMARY);
}
return col.compare(strA, strB );
}
}

La recompilation de Spring Security ne pose pas de problème, et il suffit de remplacer les jar originaux par ceux générés...

2. Savoir lire les petites lignes
Cette fois-ci, pas d'exception dans la console... mais toujours pas de page d'authentification :-/.
J'avoue avoir pas mal buté sur celui là, mais pour la faire courte, la solution... se trouve dans la doc de GAE/J :

"Note: Static files, files that are served verbatim to users such as images, CSS or JavaScript, are handled separately from paths mentioned in the deployment descriptor. A request for a URL path that matches a path to a file in the WAR that's considered a static file will serve the file, regardless of servlet and filter mappings in the deployment descriptor. You can exclude files from those treated as static files using the appengine-web.xml file."

Première (vraie) leçon : sur le cloud, les ressources statiques sont traitées à part. Donc inutile de tenter de les filtrer avec un servlet filter, ça ne marche pas (au passage, les modification suggérées ci-dessus dans le appengine.xml n'ont pas marché) :-(
Ce qui au final s'avère très gênant pour ma solution GWT : ainsi, les pages HTML puis le code Javascript, qui sont le cœur de mon application, étant considérés comme des ressources statiques, elles ne peuvent être prises en charge par le filtre Spring Security. Seuls les appels asynchrones vers le serveurs échouent (puisque l'utilisateur n'est pas authentifié), mais c'est déjà bien tard.

3. Un contournement tiré par les cheveux
Alors, comment duper GAE ? Au final, assez simplement : il suffit de renommer notre page HTML en JSP ! Du coup, elle est filtrée, et les ressources JS ne sont chargées qu'après authentification.
C'est un hack, mais ça marche... en attendant mieux ?

Google App Engine et JPA : un support limité

Avril 22nd, 2009

Comme tout bon geek qui se respecte, voila deux semaines que je joue avec le Google App Engine.
Mon idée de base était simple : dans quelle mesure une application existante peut-elle être portée dans les nuages de GAE ?
Etant modeste et fainéant, j'ai tenté d'adapter l'application de démonstration Gilead, basée sur Hibernate.

L'application en elle-même est très simple (je venais de la simplifier lors du portage à GWT 1.6) : un utilisateur est associé à un ou plusieurs messages, l'IHM permettant d'interroger et de mettre à jour la base, le tout basée sur une simple association un-à-plusieurs. GAE n'aime pas Hibernate ! Comme vous l'avez sans doute lu un peu partout, Google App Engine est basé sur DataNucleus, une librarie de persistance JDO. Ce choix technologique surprenant (qui se justifie sans doute au niveau de l'infrastructure mise en œuvre par Google) me laisse perplexe : il semble évident que JDO est en perte de vitesse et a clairement perdu la bataille ORM contre Hibernate et JPA. Que ce soit pour de bonnes ou de mauvaises raisons est un autre débat, mais clairement, le marché a tranché en faveur de JPA. Et justement, l'argument massue est que DataNucleus fournit aussi une surcouche JPA. Massue, vous avez dit?... Voyons voir ça ! JPA-dry Me voila donc avec mes entités JPA marchant parfaitement avec Hibernate. Qu'en est-il avec DataNucleus?.. Première difficulté : la gestion des ID. Sur ce point, DataNucleus est assez tatillon : * Pas de support de la stratégie d'ID "AUTO", passage obligatoire en "IDENTITY" * Pas de support des ID entiers (Integer) : d'après cette page, seules les clés Long, String ou Key sont acceptées... * Idem pour mon champs "Version" (passage en Long obligatoire) Pour les reste du mapping, c'est à peine plus simple, notamment concernant les associations. En convertissant l'ID en Long, j'ai eu le droit au message suivant : Error in meta-data for net.sf.gilead.sample.domain.Message.id: Cannot have a java.lang.Long primary key and be a child object (owning field is net.sf.gilead.sample.domain.User.messageList) D'après la doc, les clés entières ne peuvent être utilisées que pour les entités sans associations, sinon il faut utiliser une instance de la classe Key, ou une String encodée. Ayant pour but de faire fonctionner mon application avec GWT, j'opte donc pour la seconde solution : @Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="ID")
@Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true")
private String id;
Dernière petite tracasserie, j'ai eu le malheur de renseigner l'entity-name de ma classe (@Entity(name="Message")), ce qui génère des erreurs aux messages peu explicites... Ouf, mon mapping est enfin terminé. Avouez que l'on est loin de l'objectif de JPA, un standard portable sur toutes les implémentations ! EJBQL, lui aussi loin du compte... Première info : il faut passer le nom complet de la classe dans vos requête, sinon DataNucleus soulève une ClassNotResolvedException : "select user from net.sf.gilead.sample.domain.User user where user.login=:login" De même, la clause 'select' est obligatoire (comme quoi, je doit être intoxiqué par Hibernate :>>) Par contre, comme indiqué dans la doc, il faudra apprendre à se passer des jointures. Ouch ! Moi qui avait pour habitude de définir mes chargement par ce biais, je suis bon pour charger explicitement mes associations une à une en appelant les getters qui vont bien. Conclusion Autant l'avouer, je suis un peu refroidi par le support JPA fourni par Google App Engine. L'implémentation retenue manque de souplesse, et s'avère relativement contraignante à mettre en oeuvre. A ce point, il me semble inenvisageable de porter une application JPA existante sous Google App Engine sans un gros travail. Par contre, une fois connue la bonne méthode, utiliser JPA pour développer une nouvelle appli GAE me semble relativement pertinent (autant que de devoir apprendre JDO...) Note importante : Comme indiqué par Erik dans les commentaires, ces limites proviennent pour l'essentiel des limites du DataStore de GAE, et non de DataNucleus, dont le support JPA est bel et bien certifié. Et la suite... Pour autant, mon périple n'est pas fini. Si j'ai à peu près réussi à dompter DataNucleus, il reste un problème de taille : l'envoi des entités JPA/DataNucleus vers GWT echoue systématiquement avec le message suivant : Type '[Ljava.lang.Object;' was not included in the set of types which can be serialized by this SerializationPolicy or its Class object could not be loaded. For security purposes, this type will not be serialized.
Problème de sérialisation GWT et de classes "enrichies" (enhanced), autant dire le syndrome Hibernate4GWT B) Je suis assez surpris que ce point n'aie jamais été testé par les équipes GAE et/ou GWT, et je travaille pour l'instant avec l'un des auteur de DataNucleus pour voir comment contourner ce problème. Stay tuned !

Coup d'oeil sur JavaFX

Janvier 24th, 2009

Pendant mes vacances de Noël, j'ai décidé de m'éloigner quelques jours de Gilead pour me pencher un peu sur le berceau de JavaFX, avec comme idée de voir un peu ce que le framework de Sun avait dans le ventre.

Première constatation : la grande majorité des articles consacrés au sujet se limitent à aborder le FX Script, le nouveau langage promu par Sun. Comme indiqué dans ce billet, cette démarche est assez réductrice... ou pas :))
Pour l'instant, jetons un oeil à ce fameux "nouveau langage"...

FX Script
Grossièrement, le FX script se divise en deux parties : une partie descriptive tout d'abord, permettant la définition d'une IHM (un peu à la manière du MXML de Flex ou du XAML de Silverlight).

SwingFrame {
title: "JFX Image Search"
height: 500
width: 500
visible: true
}

L'idée est plutôt bonne, car elle permet vraiment de séparer la présentation de l'IHM de son fonctionnement, ce qui est un manque évident des solutions à base de Swing, et que les pages JSF avaient également du mal à prendre en charge avec leurs tags spécifiques.
On comprend ainsi mieux pourquoi Sun développe un outil orienté designer pour JavaFX (cf. billet précédemment cité).
A ce titre, la démarche de Sun est très similaire à celle d'Adobe (avec Catalyst) ou celle de Microsoft (avec Expression), qui proposent également cette séparation entre code et présentation via un langage dédié et un logiciel orienté designer.

Notez au passage que ce langage descriptif n'est en gros qu'une surcouche textuelle à Swing : on y retrouve sans peine nos chers layouts et composants graphiques classiques.

L'autre partie réside dans le langage de script lui-même, et là, je suis beaucoup plus dubitatif. Tout d'abord, pourquoi un nouveau langage ? Java ne suffisait-il pas ? Il est d'autant plus permis d'en douter que contrairement à ce que son nom pourrait faire croire, le FX Script est un langage... fortement typé et compilé, en bytecode qui plus est !
Il est d'ailleurs parfaitement possible de faire cohabiter du code Java et FXScript dans un même programme.

De plus, FXScript, loin d'être une évolution/simplification de Java, s'en éloigne incompréhensiblement (ajout de mot clés inexistants en Java, distinction floue entre action et function, etc...). Les Sequences et leur cortège de nouveaux mots clés ajoutent à ce titre une complexité indéniable à l'ensemble.
Au final, le code JavaFX n'a plus grand chose à voire avec du Java, ce qui ne facilitera pas son adoption par la base établie.

The applets strike back
Suivant encore une fois l'exemple de Flex et de Microsoft, les application JavaFX s'exécuteront dans un plug-in dédié au sein du navigateur. Cela ne vous rappelle rien ? Mais si, les applets, bien sûr. Comme quoi, Sun n'a pas toujours eu tort ;-)

L'idée est toujours de faire tourner une application Swing à l'intérieur du navigateur. Pour les nouveautés, cela semble se limiter à quelques composants et un nouveau skin. C'est tout et c'est assez peu, même si en effet, le framework graphique de Sun est déjà bien fourni.

Enfin, la communication avec un serveur d'application me semble la grand absente pour un framework qui se veut RIA. Seules possibilités : utiliser RMI ou appeler un Web Service.
La première solution est proprement inenvisageable pour des applications web (ne serait-ce qu'au niveau des contraintes de sécurité). La seconde est connu pour des volumétries de réponses importantes et une compléxité qui ont favorisé l'émergence de REST (non supporté par JavaFX).
A ce titre, on se souviendra que BlazeDS a été conçu et "vendu" par Adobe comme le remède aux piètres performances des appels WS...

Conclusion
En parcourant les différents articles et présentation JavaFX, une question lancinante revenait sans cesse : pourquoi JavaFX ? Quelle est l'avancée, le petit plus, l'argument décisif qui ferait de ce framework un choix incontournable ?
Autant j'ai quelques éléments de réponse pour GWT, Flex ou Silverlight, autant JavaFX me laisse sans idée à défaut de sans voix.

Au final, JavaFX s'inscrit dans la tendance initiée par Flex et Silverlight, mais ne propose pas d'innovation majeure, et avec un retard certain (au moins 2 ans). Je suis assez dubitatif sur ses qualités intrinsèques, et s'il reste quelques bonnes idées, j'attends de voir un peu comment la sauce va prendre et si une killer app me fera revoir ce jugement mitigé...

Tout Valtech à la carte… gratuitement !

Décembre 29th, 2008

Petite news business : Valtech organise des séminaires gratuits sur Toulouse, ouvert à tous et à toutes.

On y causera Web 2.0, Agile, Architecture, Industrialisation…
En fait, c’est vous qui choisissez le sujet lors de l’inscription en ligne (http://www.valtech.fr/fr/index/news/seminar.html).

Le premier séminaire aura lieu le 3 février 2009, les autres suivront tous les 2 mois environs.

Pour ma part, j’interviendrai surtout sur les sujets RIA et architecture Java, alors, votez et venez :>> !

PS : un grand bravo à François, qui a remué de lourdes montagnes pour qu’un tel événement puisse avoir lieu.

Gilead: send your Hibernate entities... well... wherever you want :-)

Décembre 20th, 2008

18 months (and 15 000 downloads) have past since release '0.1' of Hibernate4GWT. It was a lot of work, testing, and thanks to feedback from the community, it seems that the library has proved its utility.

It was time to evolve, so I added Flex/BlazeDS support to the library (since Adobe framework suffers of the same issues) and two useful annotations (@ServerOnly and @ReadOnly) for a better control of the clone and merge operations.
I also renamed the project to Gilead (for Generic Light Entity Adapter) to reflect a more general purpose than just Hibernate and GWT, refactored the project (split in modules) and change of web site (god bless CMS :D )

And the future is even more exciting : thanks to Vincent Legendre, release 1.2.1 will add Comet support and a new adapter for XML. Alexandre Pretyman adds UnionCustomBeanTransformer to control object transformation, and Aymen Sayhi provides Maven support. All of them are planned for January 2009
On my side, I am working on Hibernate interface loading (to simplify object graph loading, without lazy loading and LazyInitializationException) and other JPA implementations, such as EclipseLink and OpenJPA, is still planned for mid 2009.

So, stay tuned B)

JBoss Messaging : a kind of magic…

Juillet 30th, 2008

Je suis actuellement sur une mission de mise en oeuvre d’un environnement full JMS. Dans ce cadre, nous avons été amené à effectué quelques tests de charge, en nous basant notamment sur ceux fournis par JBoss Messaging 2.0 (encore en alpha version, mais les performances annoncées sont proprement ahurissantes !).

Pourtant, en cours de campagne, un doute m’étreint. Les chiffres obtenus par un client de test me semblent bizarrement incohérents avec ceux de ses congénères, pourtant codés de manière identique.

Je cherche, je tourne, j’expérimente, jusqu’à en arriver à mon test ultime : écrire volontairement une erreur et vérifier qu’elle est bien détectée par l’application.

Mon fichier « jndi.properties » est parfaitement classique (c’est d’ailleurs celui que l’on trouve en premier en cherchant sur Google):
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.provider.url=localhost:1099
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces

Pourtant, lorsque je modifie le paramètre «java.naming.provider.url » avec un nom de serveur totalement faux (à moins qu’un facétieux ingénieur système n’ai nommé une machine « toto » :roll:), mon client de test marche toujours parfaitement : il envoie et reçoit des messages !

Pire, j’arrête alors mon serveur JMS et que je relance le client de test… celui-ci persiste à fonctionner correctement 88|!!!

Alors que je commence à sérieusement douter de ma santé mentale, je cherche sur le wiki JBoss , et là, surprise :

Automatic Discovery of HA-JNDI Servers

The org.jnp.interfaces.NamingContextFactory also features the ability to automatically discover naming servers running the JBoss HA-JNDI service. Discovery occurs when either no Context.PROVIDER_URL is specified, or no valid naming service could be located among the URLs specified. When discovery is attempted, a multicast packet will be sent out, requesting that any HA-JNDI servers reply with an announcement of their availability. If any replies are received, the NamingContext can then connect to the replying server.

Par défaut, en cas d’erreur ou d’absence du paramètre « java.naming.provider.url » (ce qui était le cas dans mon client de test, une bête faute de frappe), JBoss broadcaste sur le réseau à la recherche d’un autre serveur de nom. Autant dire qu’il en a trouvé sur le LAN (ils sont ici monnaie courante, notamment en environnement de développement/intégration). De plus, mon programme étant basé sur les destinations de tests créés par défaut par JBoss, tout se passait « bien » (sauf peut-être pour le serveur dont j’ai dû dégrader les performances avec mes tests de charge :oops:!!!).

Au final, il existe bien un paramètre pour empêcher ce genre de comportement, que je vous conseille donc d’inclure à vos fichiers « jndi.properties » :

jnp.disableDiscovery=true

Ca vous évitera peut-être d’envisager l’exorcisme de votre machine si elle se met à envoyer des messages alors que vous avez arrêté votre serveur JMS :>>

Hope this helps !

GWT-Ext : Quelles leçons pour GWT ?

Mai 14th, 2008

Depuis deux semaines, la communauté Open-Source en général et GWT en particulier ne parlent plus que de ça : le changement de licence ExtJS et par extension celui de MyGWT.

L’idée ici n’est pas de raconter une nouvelle cette histoire: d’autres articles l’ont déjà fait et bien fait (cf. l’excellent billet de Sami, dont je partage entièrement l’avis sur l’inélégance et l’embarras suscité par ce changement de licence), et y revenir n’apporterait pas grand-chose.

Maintenant que le fracas des échanges passionnés commence à s’estomper, cet épisode m’inspire plusieurs sentiments mitigés concernant l’écosystème GWT.

Le premier élément de réflexion est l’évidente fragilité de la plateforme. Ainsi, il suffit d’un seul changement, décidé par un seul homme, non salarié de Google qui plus est, pour faire trembler l’édifice tout entier et remettre en cause nombre de développements GWT en cours. Ce simple constant me semble édifiant, et à vrai dire, assez inquiétant.

L’élément central de cette fragilité n’est sans doute pas à chercher bien loin : du fait de l’incomplétude de GWT est née la notion d’écosystème. En effet, le noyau fourni par Google se limite essentiellement à un (excellent) compilateur Java -> Javascript, qui permet de s’affranchir de l’enfer des moteurs Javascript des différents navigateurs, et au hosted mode permettant le debug en Java du code GWT.

Pourtant, en dehors de ce compilateur, pas grand-chose : peu de composants graphiques, pas de micro-architecture MVC à la Cairngorm, une intégration avec les serveurs et technologies existantes réduites au plus strict minimum...
Le manque le plus criant concerne bien entendu les composants graphiques « natifs », dont la pauvreté est clairement rédhibitoire dans une comparaison avec Flex ou une autre plateforme Ajax. Il ne fournit pas de vrai data-grid, ni aucun composant réellement avancé…

Ainsi, pour combler les différents manques de GWT, plusieurs librairies ont vu le jour : MyGWT, GWT-Ext pour la partie graphique, GWT-SL pour l’intégration avec Spring, Gwittir, etc…

Développer une application GWT devient donc un puzzle, consistant à assembler différentes librairies Open-Source, généralement développées par des individuels, certes talentueux mais qui n’apportent pas les garanties de pérennité nécessaire à un développement commercial.

A titre de comparaison, imaginez un monde où Sun ne fournirait que le compilateur et quelques classes de base et que Swing, les servlets, RMI n’étant que des librairies fournies par de simples ingénieurs, qui pourraient tout abandonner du jour au lendemain… Effrayant, n’est-ce pas ?

Sur ce point, Flex se distingue assez nettement de la philosophie GWT : les librairies Open-Source incontournables (BlazeDS, FlexUnit, Cairngorm…) sont validées et hébergées par Adobe, ce qui assure une stabilité du framework dans son entier.

C’est d’ailleurs, à mon humble avis, de ce comportement dont devrait maintenant s’inspirer Google : fédérer les librairies jugées « indispensables », les intégrer au framework afin de l’enrichir et d’assurer la pérennité de ces briques nécessaires à tout développement.

Soyons clair : je pense toujours que GWT est un framework intéressant et riche en potentiel, avec de vrais atouts dans la bataille RIA en cours. Mais sans unification, sans stabilisation de l’écosystème, tout développement reste à la merci d’un revirement individuel, ce qui est clairement peu acceptable dans un environnement industriel où les investissements se chiffrent en dizaines voire centaines de millier d’euros.

A suivre…

BlazeDS: Use hibernate4gwt as Hibernate adapter

April 17th, 2008

Foreword

I am currently working on a EJB3 / Flex3 project and face a very familiar issue.
BlazeDS is an open-source library, published by Adobe, to send messages from Flex code to java one (and vice-versa). The source code is an extract of LiveCycle Data Services, its commercial ancestor, still alive and handling more functionalities.
One of the most missing features of BlazeDS is an adapter to send Hibernate beans from a Java server to the Flex/Action Script client side, whereas this adapter is available in LiveCycle DataService (but it costs a lot of $$$, including runtime fees).

Hibernate4GWT ?
Hibernate4GWT is a open-source library I am working on since more than one year now to allow sharing Hibernate beans from server to GWT client side. But, despites of its name, the core of the library is really independent of GWT (don’t worry, you won’t have to add GWT libraries to your Flex project ;) ):

  • The ‘clone’ operation consists of converting Hibernate beans to pure, regular beans. Proxies are removed, and Persistent collections converted to their classic counterparts. Of course, persistence information handled by proxies and persistent collections are stored to be reused when the POJO comes back.
  • The ‘merge’ operation takes the neutral bean and merges it back to a Hibernate bean. Proxies and persistent collections are regenerated accordingly to what was undone by the clone operation.

Defining your own adapter
It is pretty simple, and well documented here.
So here is the code:

/**
* Hibernate adapter for BlazeDS
* It is based on hibernate4gwt core and delegates Hibernate beans management to it.
* @author bruno.marchesson
*
*/
public class HibernateAdapter extends JavaAdapter
{
/**
* The Hibernate bean manager
*/
private HibernateBeanManager _beanManager;

//-------------------------------------------------------------------------
// Java adapter override
//-------------------------------------------------------------------------
/**
* Adapter initialisation
*/
@Override
public void initialize(String name, ConfigMap config)
{
// Call base method
//
super.start();

// Get EntityManagerFactory
//
Object entityManagerFactory = getEntityManagerFactory();

// Create hibernate bean manager
//
_beanManager = new HibernateBeanManager();
_beanManager.setEntityManagerFactory(entityManagerFactory);
}

/**
* Invoke adapter
*/
@Override
public Object invoke(Message message)
{
RemotingMessage remotingMessage = (RemotingMessage) message;

// Merge input arguments
//
List mergedParameters = (List) _beanManager.merge(remotingMessage.getParameters());
remotingMessage.setParameters(mergedParameters);

// Call Java adapter
//
Object result = super.invoke(message);

// Clone result
//
return _beanManager.clone(result);
}

//-------------------------------------------------------------------------
// Internal methods
//-------------------------------------------------------------------------
/**
* @return the Entity Manager Factory
*/
protected Object getEntityManagerFactory()
{
// JNDI Lookup for Entity Manager Factory
//
Context context = new InitialContext();

Object entityManagerFactory = context.lookup("your_emf_jndi_name");
if (entityManagerFactory == null)
{
throw new RuntimeException("Unable to find EntityManagerFactory");
}
return entityManagerFactory;
}
}

It is pretty straightforward: the only difficulty is to set the Hibernate session factory or the entityManagerFactory to the HibernateBeanManager. In our case, we use the JNDI name of the EntityManagerFactory (declared in the EJB3 “persistence.xml” file), but you can inject easily it in another way depending on your code.
The “invoke” method is the heart of the adapter: before server call, the input arguments are merged to Hibernate entities, and the result of the process call is cloned before going to the server side.

Stateful/Stateless mode
Previously, I mentioned that the persistence information hold by proxies and collections is stored in the ‘clone’ operation and reused in the ‘merge’ one.
Basically, Hibernate4GWT provides 2 ways of storing such information: on the server (the stateful mode) or on the pojo (stateless mode).

Stateful mode:
The proxy is stored in the HTTP session. As a consequence, your entity beans do not have any technical inheritance. Magic, isn’t it B)?

To make it work, just edit the Hibernate Adapter below as follows :

  • In the ‘init’ method, add the following line:
    _beanManager.setPojoStore(new HttpSessionPojoStore());
  • At the very beginning of the ‘invoke’ method, put the following line :
    HttpSessionPojoStore.setHttpSession(FlexContext.getHttpRequest().getSession(true));

Stateless mode:
The entity beans store minimal persistence information, by inheriting from LazyPojo abstract class. No information is stored on server, which can be very helpful in clustered environments.

First, your entities must inherit from net.sf.hibernate4gwt.pojo.LazyPojo:
public class MyPojo extends LazyPojo
{…}

And here is the LazyPojo class in ActionScript (your value objects have to inherits from it, in a symmetric way to your entity hierarchy) :

package com.mypackage.client.vo
{
import com.adobe.cairngorm.vo.IValueObject;
import mx.collections.ArrayCollection;

[RemoteClass(alias="net.sf.hibernate4gwt.pojo.LazyPojo")]
public class LazyPojo implements IValueObject
{
/**
* The internal lazy properties collection
*/
private var _lazyProperties:ArrayCollection;

/**
* Constructor of Lazy Pojo abstract class.
*/
public function LazyPojo()
{
_lazyProperties = new ArrayCollection();
}

/**
* Getter for lazy properties collection
*/
public function get lazyProperties():ArrayCollection
{
return _lazyProperties;
}

/**
* Setter for lazy properties collection
*/
public function set lazyProperties(value:ArrayCollection):void
{
_lazyProperties = value;
}
}
}

The future
BlazeDS is not currently supported by Hibernate4GWT : it means that it as not been tested in different configurations (remoting, messaging, etc…), and there is no dedicated distribution or documentation for Flex applications.
Nevertheless, Hibernate4GWT is currently under heavy refactoring : the code has been split in different jars, and an official new distribution and documentation is scheduled for the end of Q2. It will include GWT new tutorial, some more functionality… and probably BlazeDS support :D

Stay tuned !

Why GWT 1.5 won’t solve your Hibernate issues

November 18th, 2007

I read a lot of GWT blogs, and one common mistake about Hibernate cohabitation is to believe that it is just a syntax issue. People thinks : “GWT only support Java 1.4 syntax, that’s why I cannot send my annotated Hibernate POJO to the client side”, and to conclude : “when GWT 1.5 will be out, Java5 syntax will be supported and I will be able to send my Hibernate beans to the presentation layer”.
This is wrong, absolutely wrong!

The reason is quite simple: Hibernate POJO are not real POJO. The persistence library adds a lot of needed information, such as session factory, by creating a dynamic proxy (with CGLIB or Javassist) around your instance. When you manipulate an Hibernate POJO, you do not do it with an instance of your class, but with a instrumented, derived one!

The second step is that GWT can only serialize (to Javascript) only a subset of the JRE (basically, the java.lang and the java.util packages). That means two things:

  • Hibernate classes, such as session factory, are not defined if the GWT supported JRE, so they cannot be serialized
  • Derived classes are not Javascript serializable, even if the base class belongs to the supported JRE. That means that even if java.util.Date is supported and serializable, java.sql.Timestamp is not. Furthermore, Hibernate defines its own implementation of the Java collections (PersistentList, PersistentSet and PersistentMap), of course not supported by GWT serialization process.

This is why Javascript serialization of your Hibernate instrumented beans will always fail!

I think that will be one of the big issue of the next GWT release, because only serialization will fail, not compilation. Indeed, your Domain classes are perfectly valid for GWT serialization, but not the associated Hibernate instances.

Hopefully, solutions do exist. You can use Dozer or Beanlib to clone your Domain class (replacing Hibernate collections with their pure Java counterpart) before sending them to the GWT client side, but take care of lazy loading (clone process cause property loading by calling the associated getter).
Or you can have a look to hibernate4gwt, that will do it seamlessly for you!

Tester une application multithread

Novembre 14th, 2007

Au cours de mes missions, j’ai parfois besoin de tester et valider des applications Java massivement parallèles (comprenez : les développeurs ont utilisé plein de threads, et au bout d'un moment, plus personne ne sait qui appelle qui et dans quel ordre ;)).
Un petit tour sur le web m’a permis de découvrir quelques pépites et outils hautement recommandables sur le sujet.

Pour la théorie :

  • Multithread unit tests with ConTest : un article brillant (et je pèse mes mots) sur la difficulté de tester des applications multithread, et comment ConTest peut y aider. A lire absolument !
  • Testing concurrent software: une présentation JavaOne dédiée au sujet. Elle est largement didactique et permet de mieux cerner les enjeux et les difficultés des tests de programmes multithread.
  • Pour les plus motivés, « Java Concurrency in practice », le livre de Brian Goetz (l'un des auteurs de la JSR 166 dédiée à la programmation concurrente) est une véritable mine d’information sur le sujet. On y apprend notamment que très peu de nos codes sont réellement thread-safe (même un simple "_counter++" ne l'est pas :-/!). Enfin, il a le bon gout d’être bien écrit, récent et relativement court (350 pages, hors annexes et index).

Pour la pratique, il existe quelques outils de tests Open-Source. Ils sont pour la plupart récents, donc perfectibles, mais apportent une vraie valeur ajoutée :

  • IBM ConTest : cette librairie permet de simuler en accéléré les changements de contexte entre les différents threads de l’application. Pour cela, elle instrumente le bytecode pour y ajouter de manière aléatoire des éléments de synchronisations (yield principalement). Simple et très efficace, surtout pour les tests en "boite noire" (montée en charge).
  • Multithreaded Test Case (MTC) : permet d’écrire des tests unitaires multithread, avec une API proche de JUnit. Elle gère la séquence des tests et permet de sérieuses économies de code à écrire (cf. cette page)
  • Java PathFinder : Cette librairie, issue de la NASA, permet de tracer les changements de contexte ayant conduit à une exception, ce qui à l’usage est très pratique (plus besoin d’essayer d’imaginer ce qui a pu se passer, il suffit de le lire). Elle est particulièrement adaptée aux tests unitaires, mais par contre semble souffrir d’une réactivité quasi nulle de ses développeurs (la plupart des questions posées sur le forum de l’outil sont sans réponse).

Hope this helps !

GWT : Génération dynamique de proxy

Octobre 11th, 2007

Génération de proxy coté serveur : un classique
La génération dynamique de proxy en Java est un mécanisme relativement nouveau, même si la notion même de proxy est standardisée depuis Java 1.4. L’émergence de CGLIB tout d’abord, puis de Javassist, a permet le développement de nombreuses librairies manipulant de simples POJO (donc sans héritage technique). En fait, l’astuce consiste pour ces librairies a modifier votre POJO, par héritage, en lui ajoutant dynamiquement les propriétés nécessaires au bon fonctionnement de la librairie:
Proxy mechanism
C’est bien entendu le mécanisme utilisé par Hibernate par exemple pour instrumentaliser vos POJO et y ajouter les infos dont il a besoin.

Tentative d'évasion
Si astucieux soit-il, ce mécanisme est source de graves ennuis dès qu’il s’agit de quitter la JVM sur laquelle a été généré le proxy. Essayer par exemple d’envoyer un objet Hibernate par JMS : si le lecteur ne possède pas l’archive Hibernate dans son classpath, c’est l’erreur de désérialisation assurée…

Le problème se pose donc pour GWT, que l’on peut assimiler _ entre autre _ à une JVM Javascript. Imaginons une classe ‘Message’ que nous souhaitons instrumenter afin d’y ajouter des informations de débug, de sécurité, etc…
Pour cela, il faut étendre la classe Message en MessageProxy, grâce à Javassist par exemple :


ClassPool pool = ClassPool.getDefault();
CtClass proxyClass = pool.makeClass("MessageProxy");
proxyClass.setSuperclass(pool.get("Message"));
...
return proxyClass.toClass(getClassLoader());

Tant que la classe reste coté serveur, pas de souci. Par contre, toute tentative d’envoyer cette classe sur la lune… euh, coté client ;) se heurtera à une erreur de sérialisation :
com.google.gwt.user.client.rpc.SerializationException: Type '<Proxy>' was not included in the set of types which can be serialized by this SerializationPolicy. For security purposes, this
type will not be serialized.

Serialization Policy : vos papiers…
En effet, en GWT 1.4, la politique de sérialisation a changé. Afin d’éviter un héritage technique hideux avec IsSerializable, l’option a été prise de lister à la compilation les classes sérialisables (contenues dans les fichiers gwt.rpc générés). D’où un conflit évident avec la génération dynamique de proxy…
De plus, le processus de sérialisation GWT est foncièrement statique. Même si la politique de sérialisation n’empêchait pas l’envoi de proxy, les données supplémentaires du Proxy ne seraient tout simplement pas envoyées sur le client, car la classe Javascript ne contient pas les champs correspondant à ces données.
Serialization_js
Il faut donc générer une classe Javascript qui soit le pendant de notre proxy. Pour cela, GWT a une solution : les Generator. Ce mécanisme, relativement peu documenté, s’appuie sur le principe de deferred binding utilisé notamment par l’internationalisation de la librairie.

Pour faire court, la mise en place de generators nécessite 3 éléments :

  • Le générateur de code proprement dit : il s’agit d’une classe héritant de Generator qui manipule et enrichit les métadonnées de classe :

...
ClassSourceFileComposerFactory composerFactory = 
    new ClassSourceFileComposerFactory("", "MessageProxy");
composerFactory.setSuperclass("Message");
SourceWriter sourceWriter = 
    composerFactory.createSourceWriter(context, printWriter)
...
sourceWriter.commit(logger);
  • La déclaration du générateur dans le fichier XML de GWT (exemple) : elle associe le générateur à une ou plusieurs classes, voire à toutes celles assignables pour une classe abstraite ou interface donnée. Ce qui signifie entre autres que toutes vos classes « proxyfiables » doivent soit être explicitement et individuellement nommées dans le fichier de configuration XML de GWT, soit implémenter une interface vide qui sert de marqueur :
<generate-with class="net.sf.hibernate4gwt.rebind.GwtProxyGenerator">
  <when-type-assignable class="net.sf.hibernate4gwt.proxy.IProxy" />
</generate-with>
  • Un appel explicite à "GWT.create(Message.class)" dans le code de l’application cliente. Ce dernier point est particulièrement important car le compilateur GWT est particulièrement bien optimisé : sans appel à la méthode de création susdite, il en déduit que la génération du proxy est inutile et donc votre générateur n’est pas appelé. Un bon moyen de vérifier est de surveiller la console de lancement GWT : les classes instrumentalisées y apparaissent explicitement

generator binding

Pour plus de détails sur la mise en place de générateurs, je vous renvoie directement au billet correspondant de l'excellent blog Timepedia ou au non moins l’excellent « GWT in action » qui traitent du sujet en profondeur.

Generator coté serveur : une mauvaise idée
J’avoue avoir essayé de récupérer le proxy généré par GWT dans mon code Java. Après tout, en hosted-mode, ma classe est bien instanciée en Java, non ?
En fait, les Generator GWT sont une technologie clairement orienté couche cliente. Un appel à GWT.create coté serveur soulève une automatiquement une exception, et hacker le hosted-mode pour récupérer la classe Java impliquerait que l’application embarque le moteur Tomcat embarqué utilisé dans ce mode. Quand je vous disais que c’était une mauvaise idée :oops:

Faisons le point…
Donc pour envoyer un proxy serveur coté client en GWT, il faut donc :

  • Créer le proxy coté serveur (avec Javassist par exemple)
  • Créer ce même proxy coté client avec un Generator GWT
  • Forcer la création des proxys coté client en appelant explicitement GWT.create

Bien entendu, il est fortement conseillé de factoriser la génération de proxy dans une même classe, la cohérence du proxy coté serveur et client étant cruciale pour le passage d’une JVM (classique) à l’autre (Javascript).

Quelques détails d’implémentation
Sur le papier, les étapes ci-dessus devraient suffire à faire créer des proxy dynamiques. En pratique, il reste un dernier petit détail à régler…
Vous vous souvenez de la SerializationPolicy dont j’ai parlé il y a quelques lignes ? Depuis la version 1.4, le compilateur génère une liste de classes autorisées à être envoyées par RPC (et donc sérialisées en Javascript), stockés dans un fichier ‘gwt.rpc’.

Première constatation : les proxys créés par notre Generator y apparaissent explicitement s’ils font partie de la signature des méthodes de notre RemoteService, ce qui est assez surprenant pour une technologie orientée cliente !

Deuxième constatation, plus désagréable celle-ci : l’implémentation de SerialializationPolicy génère sa « white-list » au premier appel. Pour cela, elle tente d’instancier toutes les classes contenues dans ses fichiers ‘gwt.rpc’ associé. Il faut donc créer les proxy avant cette vérification, sous peine de faire systématiquement échouer l’appel à cause d’une ClassNotFoundException sur celui-ci…

Là aussi, il existe plusieurs solutions :

  • Générer au lancement coté serveur tous les proxys nécessaires. Dans le cas d’une interface marqueur, cela pose différents problèmes (cf. ici par exemple) assez délicats à gérer.
  • Plus simplement, j’ai développé un ClassLoader qui wrappe le ClassLoader par défaut et qui génère mes proxy à la première demande de chargement.
public Class loadClass(String name) throws ClassNotFoundException
{
  if (isProxy(name))
  {
  //	Get source class name
  //
    String sourceClassName = getSourceClassName(name);
    Class sourceClass = _wrappedClassLoader.loadClass(sourceClassName);		
  //	Generate proxy 
  //
    return generateProxyClass(sourceClass);
  }
  else
  {
  //	Load class
  //
    return _wrappedClassLoader.loadClass(name);
  }
}

Et ça marche…
J’ai fait plusieurs tests (en fait, j’utilise cette technique pour remplacer un héritage technique d’hibernate4gwt par une simple interface de marquage) et cela semble fonctionner dans le cas qui m’intéresse.
Par contre, il existe quelques points à améliorer avant de rendre un tel mécanisme réellement transparent, à cause notamment des appels explicites à GWT.create (je n’ai pas encore réussi à berner le compilateur GWT >:( ) et de l’enrobage nécessaire du ClassLoader.
Avis aux amateurs !

Java Persistance API 2.0 ... fin 2008 !

Septembre 5th, 2007

Cet été la commission de définition de JPA 2.0, estampillée JSR 317, a été créée.

Au menu, pas mal de choses intéressantes (traduction approximative ;D ):

  • Fonctionnalités de mapping O/R étendues
  • Enrichissement du Java Persistence query language
  • Création d'une API pour des requêtes de type "criteria"
  • Standardisation d'un ensemble de "conseils" pour la configuration des requêtes et de l'entity manager
  • Standardisation de metadata supplémentaires pour le support du DDL
  • Mécanismes additionnels de "pluggabilité" pour les contextes de persistance au sein d'un environnement Java EE
  • Standardisation de contrats pour le détachement et la fusion d'entités, et la gestion du contexte de persistance
  • Validation (couplé avec la JSR 303)

Ces quelques points ne sont que des propositions : la liste n'est ni close ni exhaustive.
Les plus perspicaces d'entre vous noteront que nombre de ces "nouvelles" fonctionnalités, tel l'API Criteria ou le détachement, reprennent des mécanismes déjà existants dans Hibernate.
Il semble donc que RedHat soit décidé à continuer la standardisation de sa librairie.

Autre point intéressant : le calendrier.

  • Aout 2007 : formation du groupe d'experts
  • Décembre 2007 : Revue du premier draft
  • Avril 2008 : Revue publique
  • 4ème trimetre 2008 : Release finale

Autant dire qu'il ne faut pas attendre JPA 2 pour demain. De plus, il est clairement stipulé que le planning de cette JSR est calé sur celle de la définition de J2EE 6 (JSR 316), menée en parallèle.

J'aurais voulu comparer cette roadmap JPA2 avec celle d'Hibernate, mais la page dédiée semble obsolète. Dommage...

Hibernate4gwt : 4 mois plus tard

Août 13th, 2007

(3 petit pas dans l’Open-Source)

Un petit billet un peu moins technique de d’habitude sur mes premiers pas dans le monde de l’Open-Source.
C’est donc avec insouciance que je lançais à la fin avril hibernate4gwt, petite librairie de niche prenant en charge la cohabitation de deux formidables librairies à fort caractère : Hibernate (sa persistance, sa transparence… et ses proxies !) et GWT (sa simplicité, sa souplesse… et son JavaScript !).

Première surprise : on ne dépose pas un projet comme ça sur SourceForge ! Il faut avant tout en faire la demande, exposer le but de son projet, gentiment, poliment et en anglais.

Ensuite, il faut document, packager, tester. Mine de rien, cela force un peu à organiser son travail, à le formaliser d’avantage que juste un bout de code dans son coin. Au final, j’estime passer près de la moitié du temps total sur une release à ces aspects non directement productifs.

Deuxième enseignement : cela prend du temps, beaucoup de temps. Outre le travail de développement, il faut faire un peu de pub sur les groupes de discussions, surveiller les topics pour trouver ceux que votre librairie peut solutionner, répondre aux questions posées sur le forum du projet, etc…

Troisième enseignement : c’est gratifiant :D J’avoue que quelques mots d’encouragement, postés à la fin d’un message, un simple « thanks » me remplit de contentement. Le simple fait de savoir que des gens à travers le monde utilisent le fruit de mon labeur, que celui-ci leur évite les soucis que j’ai eu à affronter, me conforte dans l’idée que la démarche est la bonne. Et puis, j’adore lire des messages de gens dont j’ignore la nationalité du prénom :roll:

Enfin, il y a la satisfaction de poster une release dont vous êtes fier. La dernière version d’hibernate4gwt commence à ressembler à ce que j’avais en tête il y a quelques mois : transparente et légère. En mode Java 1.4, les services GWT doivent juste hériter de HibernateRemoteService pour fonctionner. Pas d’autre impact sur le code 
J’ai également rajouté le support d’objets du Domaine Java5, la demande à ce sujet étant forte (tout comme l’attente du support natif par GWT) en essayant de rester là aussi le moins intrusif possible (pas de fichier de mapping Dozer, qui d’après les retours d’expérience que j’ai, ressemblent plus à une plaie qu’à une solution).

J’ai encore un peu de pain sur la planche (supprimer les derniers héritages techniques !), mais après les efforts que m’ont demandé la dernière release, je vais faire une petite pause… le temps d’écrire un ou deux articles que j’avais promis à developpez.com (non Ricky, je n’ai pas oublié ;-) ) et de finir « Jonathan Strange et Mr. Norrell », un roman aussi prometteur qu’épais :D

Stay tuned !

GWT : enrichir l'émulation JRE

Juillet 10th, 2007

Avant-propos (ou le pourquoi du comment)
La sérialisation GWT répond à une règle stricte : seules quelques classes issues de la Java Runtime Environment sont supportées, et malheur à quiconque s’en écarte. De manière cohérente, certaines classes de base sont supportées. Par contre, rien n’assure que les classes dérivées le soient.
C’est justement ce comportement, au demeurant fort logique, qui est la cause de nombreuses SerializationException lors de l’envoi de date du serveur vers le client.
En effet, il est fort probable que le moteur de persistance, peu au fait des subtilités de sérialisation Javascript, aie tôt fait de remplacer votre instance de java.util.Date par une dérivation SQL (java.sql.Date ou java.sql.Timestamp) plus proche de son modèle relationnel.
Généralement, le contournement préconisé revient à créer un objet Date standard à partir de sa variation SQL afin de ne pas heurter môssieur GWT ;). Sauf que… la classe Timestamp est plus précise que la classe Date, et donc que cette conversion peut poser quelques problèmes de synchronisation au retour sur le serveur. Je pense notamment aux malheureux qui ont basé la gestion de version sur un champ Timestamp et qui se retrouvent bien embêtés avec leur valeur de retour tronquée…
Ce problème ayant été relevé par un utilisateur de ma librairie hibernate4gwt, j’ai décidé de m’attaquer au problème.

Portage du timestamp : premier essai
Dans ma grande naïveté (je suis parfois d’un optimisme béat), j’ai cru qu’en définissant une classe d’émulation classique _ comprenez implémentant (Is)Serializable _ le tour serait joué.
Hélas, trois fois hélas, la sérialisation JavaScript semble assez mal s’accommoder de l’héritage nécessaire entre Timestamp et Date. J’ai d’ailleurs ouvert une fiche de bug (n°1164) concernant ce problème… sans réponse à ce jour :'(

Portage du timestamp : seconde tentative
Etant d’un naturel têtu, j’ai parcouru les sources de GWT à la recherche du mécanisme permettant la sérialisation de l’objet java.util.Date.
En fait, un objet émulé nécessite deux classes :

  • Une classe d’émulation JSNI, situé dans le dossier 'com/google/gwt/emul/<package-emulé>' ou un de vos répertoires propre en suivant la même nomenclature
  • Un serialiseur dédié, portant le même nom que la classe, agrémenté de « _CustomFieldSerializer ».

L’écriture de la classe d’émulation JSNI s’est avéré relativement simple, en m’appuyant sur l’existant Date, seul le champ « nanoseconds » devant être ajouté.

Le sérialiseur quand à lui répond à un certain nombre de règles plus ou moins tacites (il n’existe pas d’interface à implémenter) :

  • Il doit être situé soit au même niveau que la classe concernée, soit dans le package ‘com.google.gwt.user.client.rpc.core.<package.émulé>’
  • Il doit implémenter les méthodes « serialize » et « deserialize », celles-ci prenant en charge le type associé.
  • Si la classe associée n’a pas de constructeur vide, l’interface doit prendre en charge la construction de l’instance par le biais de la méthode « instantiate »
  • Bien entendu, les appels à la sérialisation et à la désérialisation doivent s’effectuer dans le même ordre, sous peine de mauvaise surprise
public final class Timestamp_CustomFieldSerializer
{
  public static void deserialize(SerializationStreamReader streamReader,
			         Timestamp instance)
                                 throws SerializationException
  {
    // no fields
  }

  public static Timestamp instantiate(SerializationStreamReader streamReader)
                                      throws SerializationException
  {
    Timestamp instance =  new Timestamp(streamReader.readLong());
    instance.setNanos(streamReader.readInt());
		
    return instance;
  }

  public static void serialize(SerializationStreamWriter streamWriter,
	                       Timestamp instance) 
                               throws SerializationException
  {
    streamWriter.writeLong(instance.getTime());
    streamWriter.writeInt(instance.getNanos());
  }
}

Notez bien que les méthodes utilisent et renvoie des objets de type Timestamp et non de simples objets, ce qui explique qu’il n’y ait pas d’interface générique à implémenter.
Et ça marche !

Dernière remarque : concernant le portage en Javascript d’une classe existante, c’est cette dernière qui est utilisée en Hosted Mode, et qu’il faut déployer l’application GWT en mode Web pour voir s’exécuter le code JSNI et valider son fonctionnement.