Publié par
Il y a 10 années · 5 minutes · Java / JEE

Hands on Wicket – Partie 3

Wicket est un framework plutôt en rupture avec les frameworks web actuels. Ici pas de fichier XML qui définit la navigation, pas de librairies de tag spécifiques, juste de la simplicité et du pragmatisme … tout pour nous plaire ! Wicket a choisi de ne pas mélanger les genres : les pages se font en plain html et le développement, à proprement parler, se fait en pur Java.

Enfonçons-nous un peu dans les mécanismes du framework. Cette semaine, étudions la gestion de la session, la validation de formulaires et l’intégration (toujours aussi simple) avec SpringFramework.

La gestion de la session

Le framework s’occupe déjà de mettre en session la page courante et les composants Wicket inclus dans celle-ci. Ce mécanisme permet notamment d’écrire les traitements de réponse aux événements à la Swing (onClick, onSubmit). Il est cependant très important de faire attention aux objets que l’on met en attribut des pages et composants pour éviter de voir grossir la taille de la session.

Mais Wicket ne met en session que la page courante. Or, dans bien des cas, il est nécessaire de pouvoir accéder et manipuler des données de la session afin de rendre certaines données accessibles au travers d’un enchainement de pages. Wicket nous permet bien sûr d’accéder à la session mais nous oblige à le faire proprement.

A partir de n’importe quel composant, il est possible d’obtenir la session en utilisant la méthode getSession() qui renvoit un objet Session, wrapper Wicket de la véritable session HTTP. On s’empresse donc de regarder les méthodes de cette session et on s’aperçoit que les méthodes get/setAttribute() sont « protected » et donc inaccessibles. Comment faire donc ?

Wicket nous invite en réalité à hériter de la classe WebSession pour proposer des méthodes explicites pour manipuler la session. L’avantage de cette méthode est la lisibilité et la limitation des erreurs dues à la manipulation de la session via les méthodes génériques habituelles et des clés de type String. Voici donc un exemple de session personnalisée :

public class ShoppingWebSession extends WebSession {

	private static final long serialVersionUID = 1L;
	private static final String CART_KEY = "cart";

	public XkeWebSession(Request request) {
		super(request);
	}

	public void setCart(Cart cart) {
		setAttribute(CART_KEY, cart);
	}

	public Cart getCart() {
		Cart cart = (Cart) getAttribute(CART_KEY);
		return cart;
	}

}

Ensuite, il faut dire à Wicket d’utiliser cette classe de session à la place de celle par défaut. Souvenez-vous de la classe principale dont je vous ai parlé dans un précédent article. C’est dans cette classe que nous allons redéfinir la classe qui représentera la session. Il suffit de redéfinir la méthode newSession(Request, Response) de la classe Application pour proposer notre classe de session.

@Override
public Session newSession(Request request, Response response) {
	return new ShoppingWebSession(request);
}

Et voila ! Wicket utilisera désormais votre classe pour gérer la session HTTP.

La bonne nouvelle, ce genre de ligne disparait de notre code :

((Cart) request.getSession().getAttribute("cart"));

La mauvaise, lors de l’utilisation de notre session, il faut effectuer un « cast » :

((ShoppingSession) getSession()).getCart();

Malgré cela, l’accès à la session ne se fait plus via des String mais au travers de méthodes Java qui doivent compiler, ce qui évite bien des erreurs.

Il est aussi possible d’utiliser des Helpers ou de créer une base de composants fournissant des méthodes pour accéder sans « cast » à la session.

La validation de formulaire

En ce qui concerne les champs requis, la procédure est très simple : on les définit avec la méthode setRequired(boolean) disponible sur la classe FormComponent (dont héritent quasiment tous les champs de formulaire).

TextField textField = new TextField("myRequiredField", new Model());
textField.setRequired(true);

Pour la validation plus avancée de valeur, il faut utiliser les validators qui implémentent l’interface IValidator. On ajoute ensuite aux éléments du formulaire les validations nécessaires avec la méthode add(IValidator). Wicket fournit en standard quelques validators disponibles dans le package org.apache.wicket.validation.validator (email, date, number, string, etc). Il est bien sûr possible de créer ses propres validateurs en implémentant soi-même l’interface IValidator.

Exemple :

TextField textField = new TextField("myRequiredField", new Model());
textField.add(NumberValidator.range(10L, 99L)); // Valide un entier compris entre 10 et 99

Pour effectuer des validations entre les éléments du formulaire (par exemple, si tel champ vaut telle valeur alors tel autre champ doit ….), il faut se tourner cette fois-ci vers l’interface IFormValidator ou la classe AbstractFormValidator. Quelques implémentations de base sont disponibles mais ce genre de validation reste très spécifique aux besoins de l’application.

Encore une fois, pas de fichier XML fastidieux à définir, juste du bon vieux Java :-) .

Intégration avec SpringFramework

Pour utiliser Spring avec Wicket, il faut ajouter l’extension wicket-spring et wicket-spring-annot à votre projet en ajoutant la dépendance dans votre pom.xml.

Du point de vue de la configuration Spring, il suffit de déclarer la classe principale de l’application en tant que bean avec un scope singleton.




	

	
	[.........]


On modifie le Servlet Filter dans le web.xml, en spécifiant cette fois-ci, la propriété « applicationFactoryClassName ». Cette propriété pointe vers la factory Spring qui permet l »intégration des deux frameworks :


	Application
	org.apache.wicket.protocol.http.WicketFilter
        
            applicationFactoryClassName
            org.apache.wicket.spring.SpringWebApplicationFactory
        

Puis dans la méthode init() de la classe principale de l’application (rappelez-vous celle qui hérite de WebApplication), il faut ajouter une ligne pour que l’injection de dépendance fonctionne pour les pages Wicket :

addComponentInstantiationListener(new SpringComponentInjector(this));

Ce SpringComponentInjector va utiliser la classe WebApplicationContextUtils de Spring pour retrouver le contexte Spring (ceci sous-entend que vous ayez configuré un ContextLoaderListener Spring dans le web.xml par exemple pour charger la déclaration des beans gérés par Spring). Si vous voulez charger le contexte Spring vous-même, il est aussi possible de passer le contexte Spring en paramètre du constructeur de SpringComponentInjector.

Enfin dans le code Java, il nous suffit d’ajouter l’annotation @SpringBean devant les attributs des composants (Page, Form, etc.) pour lesquels il faut injecter des beans gérés par Spring.

@SpringBean(name="mySpringService")
private MyService myService;

Et voila, rien de plus !

La prochaine fois, nous verrons la mise en œuvre des mécanismes de templating, l’internationalisation ainsi que la façon d’écrire des tests unitaires (ce qui tient à cœur aux équipes de Xebia) !

4 thoughts on “Hands on Wicket – Partie 3”

  1. Publié par djo.mos, Il y a 10 années

    Bonjour et merci pour la série Wicket ;)
    Je me permets d’émettre quelques chtites remarques/suggestions:
    En ce qui concerne la gestion de la session, et pour respecter encore plus l’approche OO de Wicket, je proposerais d’y aller en POJO, c’est à dire laisser tomber l’apptoche par Map, même en interne, et d’ajouter un attribut de type Cart dans la classe qui étend WebSession ainsi que son accesseur/mutateur.

    Encore à propos de la session, et pour éviter les casts à chaque fois qu’on récupère la session, on peut utiliser les ellipses de Java 5 et redéfinissant la méthode getSession dans la page en cours avec comme type de retour notre propore session, de cette façon:

    @Override
    public MySession getSession(){
    return (MySession)super.getSession();
    }

    Enfin, pour ce qui est de Spring, je voudrais émettre deux remarques:
    – Il faut aussi wicket-ioc.jar dans le classpath
    – Mais surtout, toute la partie XML est superflue: pas besoin de déclarer la WebApplication comme Bean Spring dans applicationContext.xml, et pas besoin de toucher au filtre de wicket: Seule addComponentInstantiationListener(new SpringComponentInjector(this)); est nécessaire ;-) (Il faut bien sûr déclarer les listeners de Spring dans web.xml comme d’habitude).

    Cordialement.

  2. Publié par jsevellec, Il y a 10 années

    Un framework web qu’il à l’air bien!!! Sa simplicité d’utilisation en proposant des comportement par défaut est vraiment intéressante.

    Cordialement,

  3. Publié par Manuel Eveno, Il y a 10 années

    Merci pour vos remarques ! Voici mes quelques éléments de réponse …

    >> En ce qui concerne la gestion de la session, […]

    C’est tout à fait possible en effet (comme décrit ici : http://cwiki.apache.org/WICKET/custom-websession-storing-objects-in-session.html) mais j’ai l’impression qu’on risque de perdre plusieurs fonctionnalités :

    • si on ne passe pas par get/setAttribute, l’objet n’est pas réellement stocké dans la session HTTP (via ISessionStore) : la réplication de session en cluster ne fonctionnera donc plus.
    • Wicket permet aussi de changer d’implémentation de ISessionStore. La encore les objets qui ne sont pas stockés par setAttribute ne seront pas vus ni stockés dans ce ISessionStore.

    Bien sur, sans problématique de cluster, la simplicité prime :)

    >> Encore à propos de la session, et pour éviter les casts […]

    En effet, on fait systématiquement hériter nos pages d’une page générique offrant l’accès à notre session. Il faut aussi le faire pour les formulaires (Form) et éventuellement les composants spécifiques que l’on développe (Component) sous peine de retrouver des casts de ce genre :
    ((CommonPage) getPage()).getSession().getCart();

    >> Enfin, pour ce qui est de Spring, je voudrais émettre deux remarques:
    >> – Il faut aussi wicket-ioc.jar dans le classpath

    C’est exact mais utilisant maven2 dans mes projets, la dépendance a été « tirée » sans que je m’en aperçoive ;)

    >> – Mais surtout, toute la partie XML est superflue […]

    Sauf si on veut injecter des beans dans l’objet application ;)
    L’héritage de SpringWebApplication permet aussi d’avoir accès à l’applicationContext de Spring et de jouer avec l’initialisation des beans via des proxy.

    En fait, j’ai juste suivi la documentation concernant Spring qui explique les différentes approches :
    http://cwiki.apache.org/WICKET/spring.html

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *