Stripes avec Spring

Stripes est un framework d'application web conçu de façon à faciliter le développement web pour un maximum de productivité. Spring est, premièrement, un conteneur de composants légers (bien qu'actuellement il fasse beaucoup d'autres choses) Il est conçu pour sa facilité d'utilisation pour un maximum de productivité. Il paraît donc sûrement naturel de les utiliser ensemble.

Stripes s'intègre avec Spring afin de fournir à vos classes ActionBean l'accès aux ressources Spring sous forme de beans Spring configurés. Il fait ceci avec une simple injection de beans Spring dans vos ActionBeans sans que vous vous en rendiez compte. Il faudra un peu de configuration pour démarrer, mais une fois faite, vous pourrez utiliser vos beans Spring dans votre application web Stripes sans écrire une seule ligne de plus d'XML !

Installation et Configuration Spring

Cette partie apporte une vue d'ensemble basique de l'installation et de la configuration de Spring afin que vous puissiez l'intégrer avec votre application Stripes. Cette partie ne traitera pas toutes les façons possible de le faire, mais tout simplement une façon qui fonctionne. Commencez par télécharger la toute dernière version du Spring d'ici : http://www.springframework.org/download. Dézippez le fichier téléchargé et trouvez le fichier dist/spring.jar. Puis copiez ce fichier dans le classpath de votre application, sûrement sous WEB-INF/lib.

Affiner Votre classpath

Si le fait d'avoir beaucoup de classes inutiles dans votre classpath vous embête, vous préférerez peut-être prendre le temps d'étudier les jar spring-* variés dans le répertoire dist afin de rassembler le minimum dont vous avez besoin pour votre application. Moi, je suis trop paresseux pour le faire donc j'utilise spring.jar qui contient plus que de nécessaire.

Une fois que le(s) jar(s) est (sont) en place il faut créer un fichier de contexte Spring, puis configurer le contexte web de l'application dans votre fichier web.xml. Commençons d'abord par jeter un coup d'œil au web.xml :

Configuration Spring dans web.xml
 
<listener> 
<listener-class> 
org.springframework.web.context.ContextLoaderListener 
</listener-class> 
</listener> 

<context-param> 
<param-name>contextConfigLocation</param-name> 
<param-value>/WEB-INF/spring-context.xml</param-value> 
</context-param> 

Le contenu du listener configure tout simplement une classe listener Spring, qui amorce Spring dans l'environnement de l'application web. Le context-param fournit à Spring le paramètre de contexte afin qu'il puisse repérer le fichier contexte à utiliser. Dans notre cas nous avons spécifié /WEB-INF/spring-context.xml. Peut importe le nom choisi, mais il est préférable que l'on place dans WEB-INF ou dans un de ses sous-répertoires, pour s'assurer qu'il n'est pas accessible par des clients de votre application web !

Le deuxième chose qu'il faut faire est de configurer le contexte Spring. Pour plus de détails lisez la documentation Spring au http://www.springframework.org/docs/reference/index.html. Ce qui suit est un exemple qu'on pourrait utiliser afin d'accéder aux composants manager variés de l'application Bugzooky en tant que beans Spring.

/WEB-INF/spring-context.xml
 
<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE beans PUBLIC 
"-//SPRING//DTD BEAN//EN" 
"http://www.springframework.org/dtd/spring-beans.dtd" 
> 

<beans> 
<bean name="/bugzooky/BugManager" 
class="net.sourceforge.stripes.examples.bugzooky.biz.BugManager"
/> 
<bean name="/bugzooky/PersonManager" 
class="net.sourceforge.stripes.examples.bugzooky.biz.PersonManager"
/> 
<bean name="/bugzooky/ComponentManager" 
class="net.sourceforge.stripes.examples.bugzooky.biz.ComponentManager"
/> 
</beans> 

Configuration Stripes pour Spring

Maintenant que nous avons paramétré Spring nous avons besoin d'informer Stripes à propos de Spring. Dans votre web.xml trouvez le paramètre d'initialisation du StripesFilter. Si vous n'avez pas encore définit le paramètre Interceptor.Classes, ajoutez ce qui suit :

L'usage de l'Intercepteur Spring (Stripes 1.5 et supérieur)
 
<init-param> 
<param-name>Interceptor.Classes</param-name> 
<param-value> 
net.sourceforge.stripes.integration.spring.SpringInterceptor 
</param-value> 
</init-param> 

Ce paramètre informe Stripes qu'il doit utiliser l'intercepteur Spring. Ceci fonctionne avec la version 1.5 de Stripes et les versions supérieures; si vous utiliser toujours une ancienne version, vous avez aussi besoin de dire à Stripes de ne pas arrêter d'utiliser l'intercepteur méthode Before/After (ce qui est configuré par défaut) :

L'usage de l'Intercepteur Spring (Stripes 1.4.x et ultérieur
 
<init-param> 
<param-name>Interceptor.Classes</param-name> 
<param-value> 
net.sourceforge.stripes.integration.spring.SpringInterceptor, 
net.sourceforge.stripes.controller.BeforeAfterMethodInterceptor
</param-value> 
</init-param> 

Nous avons fini d'installer et de bidouiller avec les configurations. Codons un peu !

Accéder aux Beans Spring dans vos ActionBeans

Stripes utilise le SpringInterceptor afin d'injecter des beans Spring dans des ActionBeans après que l'ActionBean soit instancié. Pour ce faire, il faut expliquer comment injecter les beans et quoi injecter. Plutôt que de spécifier la configuration dans un fichier XML, on utilise une simple annotation. Un cas typique ressemblerait à quelque chose comme ceci :

Injection du bean Spring dans un ActionBean
 
/** Permet au BugManager d'être injecté. */ 
@SpringBean("/bugzooky/BugManager") 
public void injectBugManager(BugManager bm) { 
this.bm = bm; 
} 

Spécifier le nom exact et chemin du bean Spring que vous voulez injecter est probablement la façon la plus sûre. Toutefois il en existe deux autres: appelons les auto-wire par nom et auto-wire par type. Si vous omettez la valeur de l'annotation @SpringBean de cette manière :

Auto-lien des beans Spring dans un ActionBean
 
/** Permet au BugManager d'être injecté. */ 
@SpringBean 
protected void setBugManager(BugManager bm) { 
this.bm = bm; 
} 

Alors, à ce moment-là, Stripes tentera d'abord d'auto-wire (auto-lien) par nom, puis par type. Tout d'abord le nom du bean désiré est dérivé du nom de la méthode - si le nom de la méthode commence par 'set', il sera enlevé et le prochain caractère sera mis en minuscule (suivant les conventions JavaBean), sinon le nom entier de la méthode sera pris tel que. Dans l'exemple ci-dessus setBugManager produit bugManager. Le contexte Spring est ensuite interrogé afin de voir s'il contient un bean appelé bugManager. Cette interrogation n'est pas sensible à la casse ni au chemin, ce qui donne le résultat du bean /bugzooky/BugManager de l'exemple ci-dessus.

Si le contexte ne contient pas de bean avec le même nom, l'auto-wire par type est la dernière chance. Dans ce cas, si un seul bean existe dans le contexte du type requit par la méthode annotée (BugManager dans ce cas), le bean sera sélectionné puis injecté dans l'ActionBean.

Un détail mérite notre attention: même si des ActionBeans eux-mêmes ne sont pas des beans Spring, avec cette approche, les beans injectés du contexte Spring sont complètement gérés par Spring et ont accès à tous les services Spring comme n'importe quel autre bean Spring.

Les Méthodes Get/Set pour beans Spring pourraient être Dangereuses

Vu que Stripes dérive des paramètres de requête et place les propriétés dans des ActionBeans par nom, il est extrêmement imprudent de fournir des méthodes public qui correspondent aux sémantiques des propriétés JavaBean pour des beans Spring (ex. public void setBugManager(...)). Ceci permettrait à des utilisateurs malveillants de rendre vos beans Spring null, ou peut-être même d'assigner des valeurs de sous-propriétés si le bean lui-même fourni des méthodes get/set.

La possibilité que cela se passe est très faible - il faudrait qu'un utilisateur malveillant devine correctement que vous utilisez Spring, les noms des méthodes qui accèdent au beans Spring, et les propriétés de vos beans Spring. Mais cela est théoriquement possible, donc autant limiter les risques (surtout que c'est très facile à faire).

Stripes fournit plusieurs alternatives. Si vous voulez auto-wire par nom, il suffit simplement d'enlever le set du nom de la méthode, par ex. public void bugManager(BugManager bm). Si vous nommez explicitement vos beans Spring avec @SpringBean("/tel/nom") alors vous pouvez simplement nommer vos méthodes autrement, ex. injecterTelManager(). Ou même encore, comme expliqué plus bas, vous pouvez rendre les méthodes protected ou private et/ou mettre l'annotation directement sur les champs afin d'injecter vos beans Spring.

Injection par Méthode vs. par Champ

Stripes peut injecter des beans Spring autant par méthode que par champ. Le type d'accès est déterminé par le placement de l'annotation @SpringBean. Si l'annotation est directement sur un champ, ex.

 @SpringBean private BugManager bugManager;

alors Stripes utilisera l'accès par champ. Si l'annotation est sur une méthode, ex.

 @SpringBean protected void setBugManager(BugManager bm) { ... }

alors Stripes utilisera la méthode afin d'injecter le bean.

Si le gestionnaire de sécurité de la JVM le permet, Stripes injectera même des beans Spring, à la demande, dans les champs et méthodes protégés, privés et ceux avec l'accès par défaut. Si le gestionnaire de sécurité de la JVM ne le permet pas, une exception qui explique le problème sera levée.

Utiliser des Beans Spring dans d'Autres Types d'Objets

Il arrive parfois que vos ActionBeans ne soient pas les seuls types de classes qui sont gérés par Stripes mais que eux aussi aient besoin d'avoir des beans Spring injectés. Les intercepteurs en ont souvent besoin, mais surtout si vous finissez par outrepasser n'importe quel Composant Configurable Stripes, vous souhaiterez peut-être lui injecter des beans Spring.

Heureusement, ceci est facile à faire. Dans le cas des intercepteurs il existe une classe mère qui peut être étendue : SpringInterceptorSupport qui est une simple classe mère qui s'injecte des beans Spring au moment de son initialisation.

Pour d'autres classes, la solution est presque aussi simple. Une classe qui s'appelle SpringHelper fournit des méthodes afin d'appliquer le même mécanisme d'injection de beans Spring à n'importe quel objet pourvu que vous ayez accès à l'ActionBeanContext, au ServletContext ou à l'ApplicationContext de Spring. Normalement il suffit d'écrire :
    SpringHelper.injectBeans(obj, StripesFilter.getConfiguration().getServletContext());