RAP, Sessions et Workbench
Le projet RAP (pour Rich Application Platform) offre la possibilité de créer des applications internet riches (RIA) J2ee en utilisant les principes et le modèle de développement des applications Eclipse RCP. N’hésitez pas à consulter ce billet pour une présentation générale de cette technologie. Pour entrer un peu plus dans les détails, Equinox (l’implémentation d’OSGI sur laquelle est basé Eclipse) fournit des bundles permettant sa propre intégration au sein d’un conteneur de servlet. RAP utilise ce support afin d’exécuter vos applications RCP coté serveur.
Lors d’un développement RAP, il faut avoir à l’esprit qu’il existe quelques différences très importantes entre ces deux cousins pourtant très proches. Par exemple, contrairement à RCP qui est généralement mono-utilisateur, une application RAP est destinée à être utilisée par plusieurs utilisateurs simultanément via des sessions HTTP. Le concept de singleton très présent dans les plugins Eclipse a donc été revu par l’équipe du projet afin d’assurer la cohérence de l’unicité d’une instance aux différents niveaux de portée classique : application, session et request. En effet, sans cette intervention, tout singleton se trouverait automatiquement au niveau application et je vous laisse imaginer les soucis engendrés quand on sait, par exemple, que le Workbench en est un. C’est donc naturellement qu’il a été placé en tant que singleton au niveau de la session. Cette assignation est faite facilement, son arbre d’héritage devant simplement inclure SessionSingletonBase qui définit tous les mécanismes adéquat dont la méthode statique suivante :
-
/**
-
* Returns the singleton instance of the specified type that is stored
-
* in the current session context. If no instance exists yet, a new
-
* one will be created. Therefore the specified type should have
-
* an parameterless default constructor.
-
*
-
* @param type specifies the session singleton instance type.
-
* @return the unique instance of the specified type that is associated
-
* with the current user session context.
-
*/
-
public static Object getInstance( final Class type ) {
-
IServiceStateInfo stateInfo = ContextProvider.getContext().getStateInfo();
-
Object result = null;
-
if( stateInfo != null ) {
-
result = stateInfo.getAttribute( getInstanceKey( type ) );
-
}
-
if( result == null ) {
-
synchronized( getInstanceLock( type ) ) {
-
result = getInstanceInternal( type );
-
}
-
if( stateInfo != null ) {
-
stateInfo.setAttribute( getInstanceKey( type ), result );
-
}
-
}
-
return result;
-
}
A noter l’utilisation du ContextProvider permettant d’obtenir le ServiceContext propre à la session de l’utilisateur dont la requête est en cours de traitement. Le ServiceContext stocke, en autres, un ServiceStateInfo qui conserve diverses informations internes nécessaires au bon fonctionnement de l’application.
Lorsqu’une session utilisateur est invalidée, la fermeture du Workbench correspondant est réalisée grâce à l’utilisation d’un SessionStoreListener qui guette toute destruction.
-
private static final class ShutdownHandler implements SessionStoreListener
-
{
-
public void beforeDestroy( final SessionStoreEvent event ) {
-
if( Workbench.getInstance().started ) {
-
Workbench.getInstance().sessionInvalidated = true;
-
Workbench.getInstance().close();
-
}
-
}
-
}
Jusqu’à RAP 1.2 RC1, le processus complet de fermeture par invalidation de session n’était pas exactement le même que celui déroulé lors de la fermeture manuelle du Workbench via la croix rouge standard. Le ServiceContext n’était pas explicitement disposé et le ServiceStateInfo qui tient le Workbench était alors toujours référencé. Dans un cas basique d’utilisation de RAP cette différence ne posait pas particulièrement de problème car seul le Thread Graphique tenait le ServiceContext et comme il était en train de se terminer, tout était bien garbage-collecté. Cependant si l’application utilisait BIRT, elle utilisait indirectement la classe XMLTypeUtil du projet EMF qui peut parfois créer une référence statique sur ce fameux Thread Graphique. Il n’était alors plus garbage collecté et tout le Workbench était conservé en mémoire malgré la déconnexion de l’utilisateur.
Notre utilisation conjointe de RAP et BIRT dans un projet en cours nous a permis de détecter cette fuite mémoire et le patch proposé à l’équipe RAP a très rapidement été intégré.