14.05.08
GWT-Ext : Quelles leçons pour GWT ?
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…
17.04.08
BlazeDS: Use hibernate4gwt as Hibernate adapter
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
?
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 ![]()
Stay tuned !
11/18/07
Why GWT 1.5 won’t solve your Hibernate issues
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!
11/14/07
Tester une application multithread
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 !
11.10.07
GWT : Génération dynamique de proxy
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:

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.

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

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 ![]()
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 !
05.09.07
Java Persistance API 2.0 ... fin 2008 !
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
):
- 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...
13.08.07
Hibernate4gwt : 4 mois plus tard
(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
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 ![]()
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 ![]()
Stay tuned !
10.07.07
GWT : enrichir l'émulation JRE
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.
08.07.07
Les limites du "miracle GWT"
GWT au pays des Bisounours
Sentez-vous ce parfum d’euphorie un brin béat qui traverse le web à la seule évocation de GWT, cette propension à parer de qualités la librairie de Google parfois au mépris de toute objectivité ? Ressentez-vous à la lecture des différents articles de présentation l’ombre de ce doute face à cette librairie, dont on vous promet monts et merveille sans contrepartie ? Parfaite intégration avec l’existant, productivité multipliée par 5, la fin des migraines Javascript ? Se peut-il vraiment que tout soit aussi parfait ?
Non, bien sûr que non…
J’avoue ne pas être d’un nature très « groupie », et donc que les superlatifs provoque en moi au mieux un haussement de sourcil interrogateur, et bien plus souvent un réaction de méfiance face au merveilleux que l’on me promet.
Soyons clairs : je pense, je suis persuadé que GWT est une bonne librairie, un très bon outil. Pas moins. Pas plus non plus. En effet, elle résout nombre de problématiques liées aux développements Ajax et Web 2.0.
De là à dire qu’elle ne souffre d’aucun défaut, il y a un pas que je me refuse à franchir. Explication en détail…
Le syndrome de la librairie magique
Comme toute nouvelle librairie, les avantages de GWT ont tendance à en cacher les limites. Souvenez-vous du précédent « Hibernate » : l’accès aux bases de données devenait tellement plus simple que l’on a cru, à tort, que la gestion de la persistance se ferait sans mal, et mieux, sans avoir besoin de comprendre les mécanismes sous-jacents tels que transactions, jointures et accès concurrents.
On assiste ici au même phénomène : la création d’application Ajax est rendu tellement plus abordable avec GWT que l’analyse s’en arrête aux bienfaits sans en mesurer les coûts et les pièges. Ne vous faites pas d’illusion : une connaissance, même partielle, de Javascript et des mécanismes associés reste nécessaire pour éviter toute déconvenue.
A mon avis, GWT, tout comme Hibernate ou Java en son temps, résout 80% des problèmes, mais crée 20% de problématiques nouvelles et complètement inédites qu’il serait malhonnête d’ignorer ou de passer sous silence.
Le mythe de l’intégration avec l’existant
L’un des arguments les moins recevable pour le modeste auteur d’hibernate4gwt que je suis est de dire que GWT fonctionne sans peine avec les applications existantes.
C’est bien entendu parfaitement faux avec toute application Spring-Hibernate (pour rester sur des technos éprouvées).
Le simple fait que GWT ne supporte que la syntaxe Java 1.4 pour la partie JavaScript de l’application interdit d’utiliser annotations et collections typées, pourtant largement répandues dans nos applications. Il en découle l’impossibilité pure et simple d’utiliser les entités du Domaine dans la partie cliente d’une application GWT.
Face à ce problème, la communauté se contente pour l’instant de pis-aller. La plus répandue consiste à convertir les entités du modèle en DTO (data transfer objects) par l’entremise de Dozer, ce qui implique l’apprentissage d’une nouvelle librairie et l’écriture des fichiers de mapping idoines. Avouez que pour une intégration naturelle, on peut repasser…
Et encore, je vous fais grâce des problématiques propres à la coexistence des entités Hibernate avec GWT
. Pour plus d’info sur ce sujet, je vous renvoie au site web d’hibernate4gwt, où j’ai posté un article sur le sujet…
Une documentation anémique
A la différence d’Hibernate ou de Spring, deux poids lourds de l’Open-Source cités plus haut, GWT ne bénéficie que d’une documentation, disons, sommaire. Quelques pages web couvrant les cas standard, mais rien concernant de manière exhaustive le fonctionnement de la librairie.
C’est d’autant plus gênant que cette librairie contient de nombreux concepts innovants qu’il conviendrait de mieux documenter (je pense notamment aux Serializer et aux Generators) afin d’en encourager l’adoption, l’usage et l’enrichissement.
Heureusement, quelques passionnés, tel Robert Hanson, ont pris le taureau par les cornes et entrepris un large travail de vulgarisation que ce soit sur leur site web (dont Timepedia est sans doute l’un des meilleurs exemples) ou par le biais de livres (le célèbre et pour l’instant relativement unique « GWT in action »), mais cela peut difficilement avoir la valeur d’un manuel de référence estampillé « officiel », nécessaire à l’adoption par le plus grand nombre.
Dans la jungle des widgets
Les composants graphiques GWT affichent une situation paradoxale : le framework en lui-même en propose relativement peu (cf. Kitchen Sink, la démo "officielle" GWT), et les librairies de composants tierces (Open-Source ou non) sont pléthores.
Pourtant, autant on peut faire confiance au caractère professionnel des widgets natifs GWT, autant on est en droit de s’interroger concernant les autres composants : quelle est leur fiabilité ? Leur pérennité ? Gèrent-ils convenablement l’arbre DOM ?
Bref, des interrogations que l’on ne peut évacuer d’un simple haussement d’épaule et qui demanderait une évaluation minutieuse, ou l’émergence d’un standard de fait, ce qui ne semble pas encore le cas.
Programmation graphique : un pas en avant, un pas en arrière ?
Avec sa programmation entièrement en Java et événementielle, GWT nous renvoie un peu à l’ère de Swing, avec ses avantages et ses inconvénients.
Au rayon des premiers, la programmation graphique retrouve enfin son unité, face aux innombrables lnagages nécessaires à une page JSF-Ajax (HTML, JSP, JSTL, Javascript,…)
Concernant les défauts, on notera la fin de la séparation entre design et programmation, tel qu’il avait été introduit justement avec les JSP/JSF.
Plus important, GWT n’est pas structurant en terme de programmation. Il n’existe pas à ce jour de framework standardisant la programmation d’une action, ou les règles de navigation, à la manière d’un Struts en son temps…
Du coup, chaque développeur a l’embarras du choix pour coder le comportement de son application, et au vue du forum officiel, nombreux sont ceux qui sont embarrassés par ce choix
. De plus, les projets y perdent en homogénéité, et donc le retour d’expérience sur telle ou telle pratique sont plus lents.
Conclusion
Ces manques, ces défauts, ne doivent pas vous faire perdre de vue mon propos premier : GWT est un très bon outil, sans doute le meilleur disponible concernant le développement d’application Ajax.
Mais au milieu de toute cette vague d’enthousiasme parfois sans recul, il me semblait important de ramener GWT à un juste statut : celui d’une librairie, utile et productive certes, mais dont l'adoption ne se fera pas sans impact...
08.06.07
Hibernate et la sérialisation XML sont dans un bateau
Introduction
Voilà un sujet qui a fait couler beaucoup d’octets. On ne compte plus les pages web, les blogs et les forums emplis de suppliques et de rage à la recherche d’une solution simple pour sérialiser simplement un objet Hibernate partiellement chargé dans un fichier XML. Comme d’habitude, c’est la faute à la LazyInitializationException ![]()
A mon tour, je me jette dans l’arène du web, en apportant ma modeste solution à cet épineux problème. Récit d’une après-midi chargée…
Hibernate & dom4J : le couple maudit
Solution intégrée à Hibernate à défaut d’être native, le support dom4J semble la première piste naturelle à explorer. Autant le dire tout net et économiser votre temps et le mien : oubliez-là illico !
Si elle remplit correctement son office (sous réserve d’absence de dépendance circulaire, sinon, c’est la Stack Overflow assurée)_ comprenez un fichier XML décrivant notre objet est bien généré_ cette solution a l’inconvénient majeur de lazy-loader à tour de bras !
En fait, dom4J se comporte comme tout sérialiseur XML standard ayant accès à la session Hibernate : chaque appel à une propriété non chargée génère une requête en base de données, et l’on se retrouve donc avec la totalité du graphe d’objet en mémoire, ce qui est à peu près l’inverse du but recherché.
Le moral en berne face aux 6 secondes et 47 requêtes SQL générées pour sérialiser un objet métier réaliste, j’ai bombardé Google de requêtes combinant « Hibernate », « XML » et « serialization ». Pas mal de pages plus tard, j’ai enfin commencé à apercevoir une lueur d’espoir…
Gaving King saved my day
Et le porteur de lumière est le père d’Hibernate en personne. Non pas qu’il ait donné une solution tout faite, mais la réflexion suivante m’a mis la puce à l’oreille :
"This is *clearly* a problem of the XML serialization framework not having sufficient extension points.
It is absolutely not our fault.Please people - get off the crackpipe!"
Oubliez la remarque sur le crack
. Le point important réside dans la possibilité d’étendre la sérialisation XML afin de l’empêcher d’accéder aux propriétés non chargées du graphe d’objet.
C’est exactement le mécanisme que permet BeanLib pour le clonage d’objets (et mis en œuvre sur hibernate4gwt). Ne me restait plus alors qu’à trouver la librairie de sérialisation XML offrant cette même possibilité d’extension.
300 millions de librairies… et moi, et moi, et moi !
Un rapide coup d’œil à cet article montre que le web regorge de libraires de sérialisation XML. Je décidai de me concentrer sur les sérialiseurs runtime afin de ne pas avoir à gérer des DTD et autres fichiers générés.
XStream, JOX ne faisant pas l’affaire, je tombais sur Apache Betwixt, et au détour de la Javadoc, sur la classe ValueSuppressionStrategy. Exactement ce que je cherchais !
And the code is…
Après quelques essais et menues adaptations, le petit bout de code suivant a été mis au point : il permet la sérialisation XML de mon objet partiellement chargée. Pas de lazy-loading, ni de LazyInitializationException.
Par avance, toutes mes excuses pour le formattage, je cherche encore la bonne option pour poste mes bouts de code ![]()
La classe qui permet d’étendre le comportement de la librairie :
class HibernateValueSuppressionStrategy
extends ValueSuppressionStrategy
{
@Override
public boolean suppressAttribute(AttributeDescriptor descriptor,
String attribut)
{
// useless : always return false
return false;
}
@Override
public boolean suppressElement(ElementDescriptor element,
String namespaceUri,
String localName,
String qualifiedName,
Object value)
{
if (Hibernate.isInitialized(value) == false)
{
return true;
}
try
{
PropertyDescriptor property =
BeanUtils.getPropertyDescriptor(value.getClass(), localName);
if (property == null)
{
return false;
}
Object propertyValue =
property.getReadMethod().invoke(value, (Object[])null);
return (Hibernate.isInitialized(propertyValue) == false);
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
}
Il faut aussi redéfinir la stratégie de stockage des ID (sinon l’accès à la méthode hashCode d’une association non chargée génère une exception)
class HibernateIdStoringStrategy extends DefaultIdStoringStrategy
{
@Override
public String getReferenceFor(Context context, Object bean)
{
if (Hibernate.isInitialized(bean) == false)
{
return null;
}
return super.getReferenceFor(context, bean);
}
@Override
public void setReference(Context context, Object bean, String id)
{
if (Hibernate.isInitialized(bean) == false)
{
return;
}
super.setReference(context, bean, id);
}
}
Et enfin la classe principale :
BeanWriter writer = new BeanWriter(new FileWriter(outputPath));// Hibernate lazy loading stategy adapters writer.getBindingConfiguration().setIdMappingStrategy( new HibernateIdStoringStrategy()); writer.getBindingConfiguration().setValueSuppressionStrategy( new HibernateValueSuppressionStrategy());
writer.write(object); writer.close();
My 2 cents
28.05.07
Les interfaces Java à la rescousse du fetching Hibernate
Dans mon travail de tous les jours, il m’arrive régulièrement de relire du code d’autrui. De manière générale, cela me transforme en un personnage grossier et passablement râleur (développement sans conscience n’est que ruine du code
). Néanmoins, il arrive parfois que je découvre au détour d’une classe une petite pépite d’ingéniosité, une manière à la fois simple et élégante de résoudre un problème sur lequel je me casse les dents depuis des mois ou des années. C’est donc le sourire aux lèvres que je vous livre ma dernière trouvaille ![]()
Chargement partiel Hibernate
Le chargement partiel des objets Hibernate (la fetching stategy) est un problème récurrent des développements mettant en œuvre cette librairie. La méthode que je préconise généralement au nom de ma société est de définir des méthodes de chargement explicites :
public void loadPerson(…) public void loadPersonAndAddress(…)
La critique la plus souvent formulée réside dans la quantité de code nécessaire au bon fonctionnement de cette solution : chaque méthode doit être écrite individuellement, ce qui présente une charge de travail non négligeable, surtout comparée aux chargements « lazy-loadés » (rien n’est chargé à priori) ou « eager-fetchés » (tout est chargé) qui présentent l’avantage de n’avoir qu’un seul point d’appel.
Utilisation des interfaces
L’utilisation conjointe des Criteria et des BeanUtils de Spring permet d’unifier les appels à la DAO avec la signature suivante :
public void loadPerson(…, Class fetchingInterface)
C’est l’interface qui porte les propriétés à charger :
public interface IPersonneAndAdresse
{
public Address getAddress() ;
public void setAddress(Address address);
}
On peut définir autant d’interface de chargement que de combinaison envisagée.
La méthode de chargement Hibernate ajoute quant à elle les jointures grâce à l’introspection :
PropertyDescriptor[] descriptors =
BeanUtils.getPropertyDescriptors(fetchingInterface);
// Add fetching criterion
for (PropertyDescriptor descriptor : descriptors)
{
criteria.setFetchMode(descriptor.getName(), FetchMode.JOIN);
}
C’est tout simple, mais néanmoins assez puissant. D’une part, cela permet de manipuler les jointures par simple ajout/suppression de méthodes dans l’interface, sans toucher au code de chargement Hibernate. On gagne en robustesse, et cette opération devient possible même pour un développeur ne maîtrisant pas les arcanes du fetching Hibernate.
D’autre part, il n’est nullement nécessaire que la classe du Domaine n’implémente cette interface. Dans ce cas, l’interface peut se concevoir comme un simple marqueur de chargement. Cependant, l’héritage permet caster la valeur de retour du chargement en cette interface, et ainsi éviter toute LazyInitializationException ultérieure.
That’s all folks !
30.04.07
hibernate4gwt
Juste quelques mots pour vous annoncer la version 0.1 de hibernate4gwt (previously LazyKiller
), hébergée sur SourceForge.
La solution à base de DTO Dozer ne me satisfaisant pas, j’ai décidé de publier mon code sous licence Open-Source, en espérant ainsi faire avancer le débat et simplifier la vie de mes confrères.
Pour information, je travaille en ce moment sur la V0.2 qui devrait permettre (en mode stateful dans un premier temps) de ne plus avoir besoin de l’héritage technique courant.
Hope this helps !
26.04.07
Hibernate ou JPA : l’embarras du choix ?
Pour un nouveau développement, vous préconiseriez plutôt d’utiliser Hibernate ou JPA ?
De prime abord, la question peut sembler étrange tant Hibernate semble avoir inspiré le standard Java Persistence API. Les technologies sont même tellement proches qu’un tel choix s’avère presque paradoxal : après tout, Hibernate est une implémentation de JPA, cela revient donc à comparer un Coca et un Coca light ![]()
De prime abord, JPA semble le choix évident. C’est en effet une API standard, et donc interchangeable. Tout développement JPA fonctionne donc sans peine tant sur Hibernate que TopLink, OpenJPA, …
C’est tout ? Eh bien, oui, c’est tout ce que JPA apporte au monde Java.
Voyons maintenant ce qu’il enlève à un développement Hibernate.
En effet, JPA étant le plus petit dénominateur commun entre différentes librairies de persistance (Hibernate, TopLink, …), certaines fonctionnalités Hibernate ne font pas partie du standard et ne sont pas supportées par les autres implémentations (ou alors de manière tout aussi propriétaire).
On peut citer pêle-mêle (liste non exhaustive) :
- Les API Criteria et Example
- La gestion du flush manuel
- Mécanismes d’attachement et détachements d’objets
- Support des curseurs
- Filtrage dynamique des résultats
- Gestion du cache de second niveau
- Cascade « delete-orphan »
Certains seront tentés de coder les fonctionnalités « standard » avec l’API JPA, et de revenir à l’API Hibernate lorsque le besoin s’en fait sentir. Comportement assez étrange et hypocrite en fait, puis que le code s’en trouve tout de même lié à Hibernate ! Quitte à choisir une API, autant être cohérent sur toute l’application…
Autre point à prendre en compte : l’évolution. Le processus de strandardisation est généralement un travail de longue haleine, qui se chiffre généralement en mois et en années. Du coup, l’intégration des fonctionnalités Hibernate absente de JPA (ou de nouvelles) ne se fera sans doute pas avant la prochaine version, la JSR 313 (J2EE 6), prévue… fin 2008 !
En passant, il est intéressant que noter que la capacité d’innovation de Sun frise le zéro absolu : les dernières « révolutions » Java (JUnit, Struts, Hibernate, GWT,…) proviennent toutes du monde du logiciel Open-Source
!
Et en attendant la JSR 313, que va faire Hibernate d’après vous ? Innover bien sûr !
A ce titre, la lecture de la FAQ Hibernate est assez éclairante :
Now that EJB 3.0 is out, is Hibernate dead?
Certainly not! We got involved in EJB 3.0 because we wanted to bring ORM technology to the masses. The EntityManager API defined by JSR-220 is just an alternative way to call Hibernate, from our point of view. We hope and expect that the standardization of ORM as a central, integrated piece of the J2EE platform will drive the next wave of adoption of ORM and Hibernate.
Because the standards process is an inappropriate place to innovate new functionality, and because it takes several years to validate a new idea and then work it into a final specification document, we will continue to develop and innovate our own APIs and use them to provide cutting edge features to those users for whom portability is not a major priority. For example, we are providing the cool new filtering functionality via the traditional Hibernate Session interface. In time, new features that prove sufficiently important will probably find their way into the EJB spec.
Clairement, Hibernate va continuer à évoluer (on pense notamment à Hibernate Shards ou Validator qui viennent d’être publiés récemment), et ces modifications ne seront intégrées à JPA que plusieurs mois ou plusieurs années plus tard.
Au vu de tels arguments, la question « Hibernate ou JPA » est sans doute plus difficile à trancher que de prime abord. L’interrogation centrale réside sans doute dans la cible du logiciel à développer : nécessite-t’il vraiment de fonctionner sur plusieurs implémentations de serveur d’application ? Vos développeurs peuvent-ils se passer des fonctionnalités propriétaires Hibernate telles que les Criteria, le réattachement, ou le filtrage dynamique ?
Des réponses à ces questions dépendront sans doute la préconisation adéquate…
23.04.07
Technical tip : Using Acegi with GWT
Foreword : why english ?
In my opinion, a technical tip must be written in english to be useful to most people (not only French one). The design and architectures entries, which are longer and more complex, will probably stay in French, since I am not always very comfortable (as you will probably see it) with William Shakespeare’s language ![]()
Introduction
Acegi is a powerful security framework based on Spring that allows you to simply manage authentication and authorization.
You can find a lot of very good written tutorials on the web (like this one)
Working with GWT
Acegi works pretty well with GWT. Both the main HTML page and RPC call are filtered by the Security library with a simple configuration file.
In my opinion, the simplest way to integrate Acegi is to not manage the login page in your module, but in a separate HTML file (no need for a new GWT module!) like this one:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html">
<title>My Login Page</title>
</head>
<body>
<h2>Please log in</h2><br>
<form method="POST" action="j_acegi_security_check">
Username: <input type="text" name="j_username"><br>
Password: <input type="password" name="j_password"><br>
<input type="submit" value="Log in >>">
</form>
</body>
</html>
I have noticed only two issues, since GWT is an Ajax framework and Acegi is designed for pages applications (like JSF): session expiration and identification for unit testing.
Session expiration
Ajax applications are not classic HTML applications: the whole page is neither reloaded nor redirected to a new one. Consequently, when HTTP session expires (by default, after 30 minutes), any subsequent RPC call will fail and your application will receive… the HTML login page as error cause
!
The best way is to properly handle the failure exception in your AsyncCallback for example by parsing the error message:
public void onFailure(Throwable caught)
{
String errorMessage = caught.toString();
if (errorMessage.indexOf("403") != -1)
{
// Access denied for this role
//
Window.alert("Access denied");
}
else if (errorMessage.indexOf("Login") != -1)
{
// Session expired : display login form
//
LoginDialog.show();
}
else
{
// Other error
//
Window.alert("Error : " + errorMessage);
}
}
The login modal dialog allows the user to near seamlessly continue his navigation in the application.
Of course, you can factorize this code in a clean abstract class ![]()
Testing
Finally, GWT implements a test unit framework, based on JUnit. It permits you to test the behaviour of the client code, including asynchronous RPC calls (cf the relevant documentation). Nevertheless, the GWT TestCase has one serious limitation: since the test code is compiled in JavaScript, it must be “GWT-JRE compliant”. That means that you cannot call any third party code in you GWT test, and especially Acegi TestAuthenticationProvider.
The only way I found to make it works is to call the authentication form from the test case, throught Java Script Native Interface:
/**
* Log on Acegi with GWT
*/
protected static native void login(String user,
String password)/*-{
$wnd.open("j_acegi_security_check?j_username="+
user+"&j_password="+password, "login");
}-*/;
Every test case method must then call the loginAs method at the very beginning of its execution.
public void testSearchAgent()
{
login("admin", "admin");
…
}
It stills open an empty browser window, but the Unit test is then authenticated.
Last tip…
If you want to launch your GWT unit test from Eclipse, don’t forget to add your ‘test’ folder as source directory in the <Module>.gwt.xml file and to add the ‘src’ and ‘test’ folders in the classpath of the JUnit test (Run… ->Classpath->Advanced…->Add folder…)
23/03/07
GWT: Hibernate inside !
GWT peut-il survivre sans Hibernate ?
Lors de nos débats actuels (vous savez, ces longues palabres techniques et enflammées où chacun campe sur ses positions
) sur la libraire Google, la gestion des proxy Hibernate a une place de choix.
Au-delà des octets se pose une question de fond : à quel point une librairie émergeante doit-elle supporter des standard de marché pour être s’imposer ?
Attardons-nous quelques instants sur la question.
Son sens n’est pas de savoir si une nouvelle librairie doit fonctionner avec les milliers de lignes de code existantes de par le monde. Il semble relativement évident que toute nouvelle technologie qui nécessiterait de faire table rase de l’existant, tant au niveau formation que code produit, serait vouée à l’échec, si géniale soit-elle. Les enjeux économiques autorisent une évolution technique, pas une révolution.
Non, l’élément important est plutôt de savoir à quel point elle devrait être compatible avec l’existant… La gestion des proxies en est une assez bonne illustration : GWT doit-elle les prendre en charge, en sérialisant les objets Hibernate en JavaScript ?
Pour ma part, j’y vois plusieurs objections :
- Il ne me semble pas du ressort d’une librairie de présentation de gérer les artefacts de sa consœur qui gère l’accès aux données
- Pourquoi supporter Hibernate, et pas TopLink, JDO, Spring, JackRabbit, [insérez ici votre librairie préférée], … ?
- Le mécanisme de proxies n’est pas standardisé. Non seulement un tel développement serait donc spécifique à Hibernate, mais il serait lié à une implémentation particulière (version) de la librairie de persistance !
Inversons maintenant la question : Hibernate doit-il supporter GWT ? En fait, je trouverai assez naturel qu’Hibernate propose un mécanisme de « déproxification » des objets qu’il gère, pour répondre au besoin des Objets qui transitent entre plusieurs machines virtuelles. Cela recouvre à la fois le cas de GWT (si on considère que la librairie fonctionne sur une machine virtuelle Java écrite en JavaScript), mais aussi la sérialisation/désérialisation XML ou l’envoi sur JMS.
Au final, il est probable que GWT et Hibernate continuent à évoluer dans leurs sphères respectives. La « glue » entre l’un et l’autre reste pour l’instant une problématique à la charge de l’application. Nul doute que l’usage fera émerger rapidement de bonnes pratiques qui permettront de marier efficacement deux technologies majeures mais qui ont parfois du mal à cohabiter.
LazyKiller : le retour
Et pourtant, tout n'est pas perdu
! J'ai mis au point une petite librairie (en fait, plutôt une preuve de concept) permettant d'utiliser des objets du Domaine Hibernate partiellement chargé dans la couche de présentation GWT (et de les réinjecter dans le code serveur).
Pour les plus courageux d’entre vous, j’ai écrit un long article qui explique les problèmatiques rencontrées, celles résolues et les limitations actuelles. Bonne lecture !