En tant que développeur, on traverse les projets en passant souvent beaucoup plus de temps à faire fonctionner les outils qui sont à notre disposition qu'à écrire les lignes de codes qui implémentent les besoins... Ici vous trouverez un recueil de galères rencontrées et parfois quelques tutos...
samedi 26 février 2011
MVP et GWT
J'ai trouvé deux articles intéressants sur le pattern MVP sur ce blog:
http://www.mikaelkrok.net/le-design-pattern-mvp-et-gwt-1-introduction
et
http://www.mikaelkrok.net/le-design-pattern-mvp-et-gwt-2-mvp-en-detail
Et voilà comment on fait un billet marque page!
vendredi 18 février 2011
MDB: utiliser des EJB sécurisés
Allez encore un petit billet sur la sécurité et les MDB.
Lorsqu'un MDB reçoit un message, il peut arriver (et même dans la plupart des cas!) qu'il doive consommer des services qui sont exposés par d'autres EJB. Supposons que l'EJB en question comprenne un contrôle d'accès sur ses méthodes:
@Stateless
@Local(LocalMyService.class)
@DeclareRoles("authenticated")
@RolesAllowed("authenticated")
public class MyServiceImpl implements LocalMyService {
... et que ce service est injecté dans un MDB:
@MessageDriven(activationConfig = {
@ActivationConfigProperty(propertyName = "user",
propertyValue = "mdbuser"),
@ActivationConfigProperty(propertyName = "password",
propertyValue = "mdbpassword"),
@ActivationConfigProperty(propertyName = "acknowledgeMode",
propertyValue = "Auto-acknowledge"),
@ActivationConfigProperty(propertyName = "destinationType",
propertyValue = "javax.jms.Queue"),
@ActivationConfigProperty(propertyName = "destination",
propertyValue = "/queue/myQueue")
})
public class MyMDB implements MessageListener {
@EJB
private LocalMyService service;
@Override
public void onMessage(Message message) {
[...]
service.myMethod();
Là l'affaire se corse car les messages JMS n'embarquent pas de contexte de sécurité (après recherche et sauf erreur de ma part), et lors de la réception d'un message le MDB se verra l'accès refusé au service. Et bien c'est
presque pas grave, car la spec EJB couvre ce problème et permet la substitution de rôle avec l'annotation javax.annotation.security.@RunAs qui prend en paramètre le nom du rôle à donner au bean (ici le MDB):
@MessageDriven(activationConfig = {
@ActivationConfigProperty(propertyName = "user",
propertyValue = "mdbuser"),
@ActivationConfigProperty(propertyName = "password",
propertyValue = "mdbpassword"),
@ActivationConfigProperty(propertyName = "acknowledgeMode",
propertyValue = "Auto-acknowledge"),
@ActivationConfigProperty(propertyName = "destinationType",
propertyValue = "javax.jms.Queue"),
@ActivationConfigProperty(propertyName = "destination",
propertyValue = "/queue/myQueue")
})
@RunAs("authenticated")
public class MyMDB implements MessageListener {
@EJB
private LocalMyService service;
@Override
public void onMessage(Message message) {
[...]
service.myMethod();Il est intéressant de noter que j'ai écrit "
presque pas grave"! Presque, car le conteneur JEE sélectionné pour le projet en question était JBoss 6.0.0.Final qui est directement impacté par le bug
EJBTHREE-1945. Le sujet de bug est que le rôle est correctement substitué dans le premier appel de méthode sécurisé, mais si cette méthode fait elle-même appel à une autre méthode sécurisée , le rôle est perdu... autant dire que tout l'intérêt de RunAs est caduque!
J'ai donc demandé s'il existait un moyen de contournement et je me suis inspiré de la réponse pour mon besoin en créant un intercepteur assigné au MDB qui authentifie le MDB avant l'invocation de la méthode onMessage:
public class RunAsInterceptor {
@AroundInvoke
public Object intercept(InvocationContext ctx) throws Throwable {
SecurityClient securityClient = null;
try {
securityClient = SecurityClientFactory.getSecurityClient();
securityClient.setSimple(userName, password);
securityClient.login();
return ctx.proceed();
} finally {
if (securityClient != null) {
securityClient.logout();
}
}
}
}L'ombre au tableau est qu'une classe hors spec JEE issue de l'API propre à JBoss est impliquée (SecurityClient), je n'ai pas poussé pour trouver un moyen d'authentification avec les outils JEE stricts, si quelqu'un a ça en stock, je suis preneur! Je vous laisse le soin de définir comment paramétrer userName et password, moi je l'ai fait avec le chargement de propriétés dans le constructeur sans arguments.
L'utilisation dans le MDB:
@MessageDriven(activationConfig = {
[...]
})
@Interceptors({RunAsInterceptor.class})
public class MyMDB implements MessageListener {L'avantage de cette conception est que lorsque les gens de JBoss se décideront à résoudre ce problème qui date de 2009, il n'y aura qu'à dégager l'intercepteur et le remplacer par @Runas!
Mais bon, c'est la tour de Babel!
Message Driven Bean: configuration de l'accès à une Destination sécurisée
Pour des besoins évidents, on m'a demandé de sécuriser l'accès aux Destinations JMS (Queue/Topic), c'est à dire les composants doivent s'authentifier pour pouvoir publier ou consommer des message.
Quand la connexion est créée programmatiquement, pas de soucis:
connectionFactory.createConnection(jmsUserName, jmsPassword);
Toutefois, la question s'est posée lorsque j'ai du déployer un MDB et après avoir fouillé dans la JSR EJB spec 3.1, je n'ai pas trouvé comment indiquer dans les méta données les valeurs user et password. Puis en élargissant le champ de mes recherches avec Google, je suis tombé sur la config suivante qui répond au besoin:
@MessageDriven(activationConfig = {
@ActivationConfigProperty(propertyName = "user",
propertyValue = "mdbuser"),
@ActivationConfigProperty(propertyName = "password",
propertyValue = "mdbpassword"),
@ActivationConfigProperty(propertyName = "acknowledgeMode",
propertyValue = "Auto-acknowledge"),
@ActivationConfigProperty(propertyName = "destinationType",
propertyValue = "javax.jms.Queue"),
@ActivationConfigProperty(propertyName = "destination",
propertyValue = "/queue/myQueue")
})
public class MyMDB implements MessageListener {
D'accord, c'était quasi-évident... mais comme je trouve que ça manque un peu de documentation, je remets une couche!jeudi 10 février 2011
Aspirer un site avec wget
Ce matin je voulais pouvoir accéder à la documentation GWT offline, mais Google ne propose pas cette option, alors j'ai essayé de récupérer le maximum d'informations avec wget.
Voici les options que j'ai choisi et pourquoi je les ai choisies:
$ wget -nc -k -q -r -p -L -R community.html http://code.google.com/webtoolkit/doc/latest/
-nc: no clobber: pour que chaque ressource ne soit téléchargée qu'une seule fois
-k : pour que les liens soient transformés en liens locaux de manière à faciliter la consultation locale
-q : pour que l'affichage sur la console ne prenne pas plus de temps que le téléchargement lui même!
-p : pour que toutes les ressources nécessaire à chaque page soit également téléchargées
-L: pour éviter de télécharger tout Internet
-R: je ne voulais pas community car j'avais peur de télécharger tout le forum!
-r : recursive... of course
Voilà, c'est une note de coin de table, n'hésitez pas à apporter des précisions si elle vous semble incomplète.
lundi 31 janvier 2011
Maven Surefire: Lancer un seul test
Il est parfois fastidieux d'avoir à lancer l'intégralité des tests d'un projet ou module maven si on a besoin d'en lancer un seul. Pour éviter cela on peut indiquer à surefire quel test exécuter:
$ mvn test -Dtest=OnlyMe
On notera également qu'il n'est pas nécessaire d'utiliser le "Fully qualified name" de la classe de test, le nom de la classe est suffisant.
Plus d'infos:
http://maven.apache.org/plugins/maven-surefire-plugin/examples/single-test.html
http://maven.apache.org/plugins/maven-surefire-plugin/test-mojo.html#test
lundi 24 janvier 2011
Hibernate: valeur des paramètres et log
Toujours galère de d'analyser les logs d'une appli utilisant Hibernate car les requêtes sont paramétrées (heureusement) et les paramètres ne sont pas directement accessible (on voit des '?' en lieu et place des valeurs dans les clause where).
Pour y remédier, rien de plus simple, il faut activer les loggers d'Hibernate qui journalisent ces informations. Donc dans log4j.properties (ou sa version XML), placer la directive suivante:
log4j.category.org.hibernate.type.descriptor.sql=TRACE
Cela permettra de trouver dans les logs les messages suivants pour chaque requête paramétrée envoyée par Hibernate:
4000 [main] TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [1] as [BIGINT] - 1295864313859
4000 [main] TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [2] as [TIMESTAMP] - Mon Jan 24 11:18:33 CET 2011
4000 [main] TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [3] as [BIGINT] - 1295864313843
vendredi 1 juin 2007
WebServices EJB 3: Trop fastoche
Bon alors on va commencer par un petit tuto pour développer et publier un service web à l'aide des possibilités offertes par Java et les EJB 3, contrairement à ce qui était précedemment nécessaire, c'est très rapide et super facile... (ok ça fait un peu recette de cuisine)
Pour rappel, au niveau de l'architecture, un conteneur d'application va héberger notre service et son implémentation est assurée par un EJB sans etat (stateless).
En ce qui concerne les pré-requis, vous devez vous assurer de disposer de:
- Le JDK dans sa version 1.5
- JBoss IDE 2 beta 2 (bon jamais la version stable??) disponible en bundle ici
- JBoss AS 4.2.0GA en téléchargement ici
- Ouvrir la perspective JBoss AS
- Dans la vue "JBoss Server" : clic droit -> New server
- Sélectionner JBoss Inc/JBoss AS 4.0:

- Donner l'emplacement du serveur et sélectionner la configuration (default sera très bien pour notre tuto):

- Dans la vue "JBoss Server" : clic droit sur le serveur créé et le lancer en debug, la sortie sur la console ne doit normalement pas contenir d'erreur et finir par:
12:08:14,062 INFO [Server] JBoss (MX MicroKernel) [4.2.0.GA (build: SVNTag=JBoss_4_2_0_GA date=200705111440)] Started in 47s:812ms
Sélectionner ensuite la configuration de serveur que nou avons précédemment défini (default).
A la création du projet, nous rencontron l'erreur (de jeunesse?) suivante:
Severity and Description Path Resource Location Creation Time Id
Project TestWS is missing required library: 'E:\j2ee\jboss-4.2.0.GA\server\default\deploy\ejb3.deployer\jboss-ejb3x.jar' TestWS Build path 1180700028078 24
Ce qui signifie que le buildpath est configuré pour trouver le fichier jboss-ejb3x.jar dans un répertoire alors qu'il ne s'y trouve pas... Pas de panique, vous pouvez trouver le fuyard dans le répertoire $JBOSS_HOME/server/default/lib. Une fois le jar manquant copié dans le répertoire attendu, demander à Eclipse de rafraîchir le contenu du projet pour que la présence du fichier soit détectée et l'erreur disparaît.
- Afin que les bibliothèques nécessaires à la création de services web JBoss soient chargées, par un clic droit sur la racine du projet, faire apparaître le menu contextuel et cliquer sur JBossWS/Add JBossWS nature. Une boîte de dialogue apparaît, les options par défaut conviennent à notre exemple:

- Créons maintenant une classe de base Hello dans le package org.tbt.wstuto:
package
org.tbt.wstuto;
public class Hello {
}
- Maintenant, nous allons un peu l'agrémenter en utilisant les annotations et en ajoutant une méthode pour notre service:
package
org.tbt.wstuto;
import javax.ejb.Stateless;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
@Stateless // C'est un EJB sans état
@WebService // C'est un service Web
public class Hello {
@WebMethod
public int world(@WebParam(name="param")int iParam){
// Ca c'est spéciale dédicace pour VLT!
System.out.println("Goodbye cruel world...");
return 0;
}
}
- Nous allons maintenant configurer l'IDE pour créer notre jar gràce a la packaging configuration définie comme suit:

- Demander de créer le package par la commande Project/Run packaging configuration et le jar apparaît dans l'arborescence de votre projet.
- Reste maintenant à déployer votre service sur le conteneur: en perspective JBoss AS, faire glisser le jar sur notre serveur default dans la vue JBoss server afin de le faire apparaître dans les modules. Pour finir demander la publication du jar:

14:48:40,640 INFO [TomcatDeployer] deploy, ctxPath=/HelloService, warUrl=.../tmp/deploy/TestWS.jar-ws46100.war/
14:48:41,109 INFO [JmxKernelAbstraction] creating wrapper delegate for: org.jboss.ejb3.stateless.StatelessContainer
14:48:41,140 INFO [JmxKernelAbstraction] installing MBean: jboss.j2ee:jar=TestWS.jar,name=Hello,service=EJB3 with dependencies:
14:48:41,625 INFO [EJBContainer] STARTED EJB: org.tbt.wstuto.Hello ejbName: Hello
14:48:41,671 INFO [EJB3Deployer] Deployed: file:/E:/j2ee/jboss-4.2.0.GA/server/default/deploy/TestWS.jar
14:48:41,703 INFO [WSDLFilePublisher] WSDL published to: file:/E:/j2ee/jboss-4.2.0.GA/server/default/data/wsdl/TestWS.jar/HelloService46098.wsdl
14:48:41,781 INFO [ServiceEndpointManager] WebService started: http://localhost:8080/HelloService/Hello
Nous pouvons vérifier la présence de votre service gràce à JBoss WS
http://localhost:8080/jbossws/services :
Conclusion
C'est une bonne illustration de l'apport des annotations: nous économisons ici l'écriture d'un fichier WSDL, voire d'un fichier de déploiement WSDD. Il n'est nullement besoin non plus d'empaqueter sous forme de war, donc pas non plus de web.xml!
Trop facile...
A noter néanmoins qu'il semble que le format de l'URL du service soit figée à http://hostname/
Service/
, nous avons essayé de paramétrer un peu dans tous les sens les annotations sans résultat, si quelqu'un à les moyens d'enrichir le billet à ce sujet, n'hésitez pas...
Inscription à :
Articles (Atom)
