RAP et les Jobs – Part 2
Nous avons vu dans le billet précédent qu’il était important de bien redéfinir certaines méthodes de la classe Job afin d’éviter les fuites mémoires. Cependant, après avoir appliqué ces préconisations, il est facile de constater que les Jobs exécutés ne sont pas garbage collectés tant que la session de l’utilisateur les ayant sollicités est active.
En effet, lors de chaque exécution, le JobManagerAdapter appelle la méthode bindToSession définie ci-dessous avec le Job en argument.
-
private void bindToSession( final Object keyToRemove ) {
-
ISessionStore session = RWT.getSessionStore();
-
HttpSessionBindingListener watchDog = new HttpSessionBindingListener() {
-
public void valueBound( final HttpSessionBindingEvent event ) {
-
}
-
public void valueUnbound( final HttpSessionBindingEvent event ) {
-
try {
-
handleWatchDog( keyToRemove );
-
} finally {
-
synchronized( lock ) {
-
jobs.remove( keyToRemove );
-
}
-
}
-
}
-
…
-
};
-
session.setAttribute( String.valueOf( watchDog.hashCode() ), watchDog );
-
}
Cette méthode place en session un objet possédant une référence directe sur le Job. Malheureusement, la valeur de la clé de stockage étant créée localement, il est ensuite incapable de le retirer. Autre conséquence, le code contenu dans valueUnbound, qui peut sembler important, ne sera jamais exécuté.
Pour contourner cette fuite mémoire, je n’ai pas trouvé d’autre moyen que de patcher le code de RAP. La solution proposée consiste à indiquer au Job la clé avec laquelle il sera mis en session lors du valueBound :
-
private void bindToSession( final Object keyToRemove ) {
-
ISessionStore session = RWT.getSessionStore();
-
HttpSessionBindingListener watchDog = new HttpSessionBindingListener() {
-
public void valueBound( final HttpSessionBindingEvent event ) {
-
/* START : Patch */
-
if (keyToRemove instanceof Job)
-
((Job) keyToRemove).setProperty(new QualifiedName(null,
-
“watchDogKey”), String.valueOf(this.hashCode()));
-
/* END : Patch */
-
}
-
public void valueUnbound( final HttpSessionBindingEvent event ) {
-
try {
-
handleWatchDog( keyToRemove );
-
} finally {
-
synchronized( lock ) {
-
jobs.remove( keyToRemove );
-
}
-
}
-
}
-
…
-
};
-
session.setAttribute( String.valueOf( watchDog.hashCode() ), watchDog );
-
}
Puis de redéfinir le constructeur de notre classe de Job en ajoutant systématiquement un listener responsable de le retirer de la session à la fin de son exécution :
-
public MonJob(final String name)
-
{
-
super(name);
-
addJobChangeListener(new JobChangeAdapter()
-
{
-
@Override
-
public void done(final IJobChangeEvent event)
-
{
-
RWT.getSessionStore().removeAttribute(
-
String.valueOf(event.getJob().getProperty(new QualifiedName(null, “watchDogKey”))));
-
}
-
});
-
}
Vous pouvez suivre l’évolution de cette anomalie sur le bug-tracking du projet RAP à l’adresse suivante.