Créer une webapp java flex avec spring blazeds
Posted on 15. juin, 2009 by admin in Blaze DS, spring
Jusqu’à présent nous avons parlé de Flex en combinaison avec BlazeDS. La création de ce type de projet est rendue très simple par la bonne intégration entre BlazeDS, Flex et Flex Builder.
Dans l’univers des frameworks web java, il en est un qui s’est imposé depuis quelques années : Spring.
Adobe l’avait reconnu, puisqu’il est assez simple de configurer BlazeDS pour lui indiquer d’utiliser Spring comme container des classes java. Mais il fallait alors gérer la configuration de BlazeDS d’un côté et la configuration de Spring de l’autre.
Il y a quelques mois, Spring a proposé une nouvelle intiative permettant de simplifier l’intégration avec BlazeDS.
Dans ce billet nous vous proposons de créer un petit projet qui vous permettra d’évaluer cette nouvelle solution.
Vous aurez besoin des librairies suivantes contenues dans les projets suivants:
Création d’une webapp Flex/Java
Commençons par créer une simple webapp en suivant ce billet si nécessaire.
Note: Dans cet exemple, nous utiliserons une table d’une base de données mysql, mais nous vous laissons libre d’adapter le code à votre propre contexte.
A cette webapp, ajouter (glisser/déposer) les libraries suivantes dans le dossier lib:
- driver de votre base de données
- spring.jar (dans le dossier /dist du projet spring)
- spring-webmvc.jar (dans le dossier /dist/modules du projet spring)
- org.springframework.flex.-1.0.0.RELEASE.jar (dans le dossier /dist du projet spring-flex-integration)
- cglib-nodep-2.1_3.jar (dans le dossier /lib/cglib du projet spring)
- com.springsource.org.codehaus.jackson-1.0.0.jar (dans le dossier /projects/ivy-cache/repository/org.codehaus.jackson/com.springsource.org.codehaus.jackson/1.0.0 du projet spring-flex-integration)
Votre dossier lib doit ressembler à ceci:

Adaptation des fichiers de configuration
Maintenant que Spring est en place, c’est sa servlet qui va intercepter les appels Flex vers le serveur. Pour ce faire, nous allons remplacer le fichier web.xml d’origine par ceci:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <display-name>Spring BlazeDS Integration</display-name> <description>Spring BlazeDS Integration</description> <!-- The front controller of this Spring Web application, responsible for handling all application requests --> <servlet> <servlet-name>Spring MVC Dispatcher Servlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/config/web-application-config.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!-- Map all *.spring requests to the DispatcherServlet for handling --> <servlet-mapping> <servlet-name>Spring MVC Dispatcher Servlet</servlet-name> <url-pattern>/spring/*</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> </welcome-file-list> </web-app>
Vous avez du remarqué la référence à un autre fichier : web-application-config.xml. Dans le répertoire WEB-INF de WebContent, créez un nouveau dossier config, puis créez un nouveau fichier nommé : web-application-config.xml .
Son contenu est le suivant pour notre petit exemple:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:flex="http://www.springframework.org/schema/flex" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/flex http://www.springframework.org/schema/flex/spring-flex-1.0.xsd"> <!-- Lance le MessageBroker BlazeDS --> <flex:message-broker /> <!-- Implementation de CandidateDAO --> <bean id="candidate" class="dao.CandidateDAO" > <constructor-arg ref="dataSource"/> <!-- Exposition de CandidateDAO en tant que destination --> <flex:remoting-destination /> </bean> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/<votre base>" /> <property name="username" value="<votre compte>" /> <property name="password" value="<votre mot de passe>" /> </bean> </beans>
Remarque: Pour faire simple nous exposons directement la la classe DAO, plutôt que de créer une classe façade.
Dans ce fichier, nous devons spécifier toutes les classes dont nous souhaitons rendre les méthodes accessibles à Flex. Cela ce fait par la définition du bean dans lequel on précise le nom complet de la classe, ainsi que le nom de la destination (id=”candidate”).
Par la balise
Du coup, inutile de configurer le fichier remoting-config.xml comme on le ferait dans une projet BlazeDS classique.
Enfin, il est nécessaire de modifier dans le fichier services-config.xml (situé dans WebContent/WEB-INF/flex).
Dabord rajouter un channel par défaut:
<services> <service-include file-path="remoting-config.xml" /> <service-include file-path="proxy-config.xml" /> <service-include file-path="messaging-config.xml" /> <default-channels> <channel ref="my-amf"/> </default-channels> </services>
Ensuite, les urls d’accès aux classes de sérialisation AMF (notez le ’spring’ dans l’url):
<channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel"> <endpoint url="http://{server.name}:{server.port}/{context.root}/spring/messagebroker/amf" class="flex.messaging.endpoints.AMFEndpoint"/> </channel-definition>
Côté Java
Nous allons maintenant créer les classes java qui exposeront leurs méthodes pour permettre à Flex d’accéder à la base et de retourner son contenu.
Créez un nouveau package dao, puis créez les classes suivantes:
Classe CandidateDAO.java
package dao; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import java.util.HashMap; import java.util.Map; import javax.sql.DataSource; import org.springframework.jdbc.core.simple.ParameterizedRowMapper; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.jdbc.core.simple.SimpleJdbcTemplate; public class CandidateDAO implements IGenericDAO<Candidate> { private final SimpleJdbcTemplate template; private final SimpleJdbcInsert insertCandidate; private final ParameterizedRowMapper<Candidate> rowMapper = new ParameterizedRowMapper<Candidate>() { public Candidate mapRow(ResultSet rs, int rowNum) throws SQLException { Candidate candidate = new Candidate(); candidate.setId(rs.getInt("id")); candidate.setEmail(rs.getString("email")); return candidate; } }; public List<Candidate> findAll() { return template.query("SELECT * FROM candidates ORDER BY firstname", rowMapper); } public CandidateDAO(DataSource dataSource) { template = new SimpleJdbcTemplate(dataSource); insertCandidate = new SimpleJdbcInsert(dataSource).withTableName("Candidate").usingGeneratedKeyColumns("ID"); } }
Classe Candidate.java
package dao; public class Candidate { private int id; private String email; public void setId(int id) { this.id = id; } public int getId() { return id; } public void setEmail(String email) { this.email = email; } public String getEmail() { return email; } }
Côté Flex
Mettons en place une simple Datagrid pour afficher les résultats:
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"> <mx:RemoteObject id="ro" destination="candidate"/> <mx:DataGrid dataProvider="{ro.findAll.lastResult}" width="100%" height="100%"/> <mx:Button label="Get Data" click="ro.findAll()" /> </mx:Application>
Maintenant vous devriez pouvoir lancer le projet depuis le connecteur Tomcat et lancer l’exécution de l’application Flex.
Si tout se passe bien vous devriez voir le contenu de votre table s’afficher.
Ajout de la sécurité
Attention:Testez bien tous les niveaux d’accès de votre application avant de la déployer. L’implémentation telle que décrite ci-dessous fonctionne telle que décrite, mais n’a pas été testée au-delà.
La possibilité de gérer finement l’accès aux méthodes des services exposés est un atout de Spring/BlazeDS par rapport à BlazeDS seul.
Pour ce faire nous allons un peu modifier me projet que nous venons de mettre en place.
Ajouter de nouvelles librairies java
Sur la page, cliquez sur le lien download de la section “Spring Security” (version 2.0.4 pour nous).
Dans le répertoire dist de cette librairie, copiez les jars suivant dans le répertoire lib de notre projet:
- spring-security-acl-2.0.4.jar
- spring-security-core-2.0.4.jar
- spring-security-core-tiger-2.0.4.jar
- spring-security-taglibs-2.0.4.jar
et également aspectjrt.jar (que vous trouverez dans le répertoire lib/aspectj de la librairie spring)
Modifications de web-application-config.xml
On rajoute les namespaces concernant la sécurité:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:flex="http://www.springframework.org/schema/flex" xmlns:security="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/flex http://www.springframework.org/schema/flex/spring-flex-1.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.4.xsd">
On ajoute la possibilité d’utiliser les notations pour définir la sécurité dans le code directement:
<security:global-method-security secured-annotations="enabled" jsr250-annotations="enabled"/>On indique que l’on ajoute la sécurité au niveau du MessageBroker:
<flex:message-broker> <flex:secured /> </flex:message-broker>
On définit la classe chargé de filtrer les accès:
<security:http entry-point-ref="securedEntryPoint"/> <bean id="securedEntryPoint" class="org.springframework.security.ui.preauth.PreAuthenticatedProcessingFilterEntryPoint"/>
Puis on ajoute quelques lignes pour définir les rôles pouvant accéder aux services
<security:authentication-provider> <security:user-service> <security:user name="admin" password="admin" authorities="ROLE_USER,ROLE_ADMIN" /> <security:user name="user" password="user" authorities="ROLE_USER" /> </security:user-service> </security:authentication-provider>
Et puis on va en profiter pour faire la déclaration du service non pas dans le fichier xml, mais par annotation. Une seule des deux déclarations possible à la fois.
Transformer le bean “contact” en lui enlevant la baliser <flex:remoting-destination />
<bean id="candidate" class="dao.CandidateDAO" > <constructor-arg ref="dataSource"/> </bean>
Voilà concernant ce fichier.
Modification de la classe java exposant le service
On va maintenant pouvoir rajouter les annotations sur la classe java pour indiquer les filtres d’accès aux différentes méthodes.
Ajoutez ces annotations juste au-dessus de la classe:
@Service("candidate") @RemotingDestination(channels={"my-amf"})
Maintenant, ajoutons les annotations d’accessibilité aux méthodes, dont voici deux exemples:
/* * accessible uniquement au role ROLE_ADMIN */ @RemotingInclude @Secured("ROLE_ADMIN") public List<Candidate> findAll() { return template.query("SELECT * FROM candidates ORDER BY firstname", rowMapper); } /* * accessible à tous */ @RemotingInclude public Candidate findById(int id) { return template.queryForObject("SELECT * FROM candidates WHERE id=?", rowMapper, id); }
Il est possible d’indiquer à Spring qu’une méthode, même publique, ne devra pas être exposé à Flex, en utilisant l’annotation : @RemoteExclude
Modification de la partie Flex
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" creationComplete="onCreationComplete()" > <mx:Script> <![CDATA[ private function onCreationComplete():void { ro.setCredentials("admin","admin"); // ro.setCredentials("user","user"); } private function onLogoutClick():void { ro.logout(); } ]]> </mx:Script> <mx:RemoteObject id="ro" destination="candidate"/> <mx:HBox> <mx:Button label="Load!" click="ro.findAll();ro.findById(3)"/> <mx:Button label="Logout" click="onLogoutClick()"/> </mx:HBox> <mx:Label text="Candidate(3)={ro.findById.lastResult.email}"/> <mx:DataGrid dataProvider="{ro.findAll.lastResult}" width="100%" height="100%"/> </mx:Application>
Cette application fait appel aux 2 méthodes exposées.
Tester-la en mode admin et user pour voir la différence.
Il doit être possible de trapper l’erreur de sécurité de façon plus propre en utilisant l’évènement fault sur le RemoteObject.
En cas de comportement inattendue, fermer votre navigateur (pour enlever le swf du cache), et relancer l’application.
Le bouton logout permet d’indiquer au serveur une déconnexion de l’application. Sinon, la session est conservée et si l’on ne fait que relancer l’application (ou rafraîchir), une erreur est générée.
Encore une fois, cette application semble fonctionner correctement. Mais indiquez en commentaires les erreurs que vous pourriez rencontrer.
Note: Ce billet a été écrit en se basant sur ce billet ainsi que celui-ci.

Maod
03. sept, 2009
Je n’arrive pas à voir le contenu de votre table s’afficher bien qu’aucune erreur n’est affichée.
débutant
30. sept, 2009
Bonjour,
Je suis en train de suivre votre tuto, mais je suis bloqué à la première exécution de l’appli. Au moment de lancer Tomcat, j’ai cette erreur :
GRAVE: Error loading WebappClassLoader
delegate: false
repositories:
/WEB-INF/classes/
———-> Parent Classloader:
org.apache.catalina.loader.StandardClassLoader@10b4b2f
org.springframework.web.servlet.DispatcherServlet
java.lang.ClassNotFoundException: org.springframework.web.servlet.DispatcherServlet
g
30. sept, 2009
j’ai demandé de l’aide un peu vite, j’utilisais les lib de spring deux fois
Bertrand
03. nov, 2009
Je rencontre actuellement des problèmes.
J’ai appliqué la méthode sur un service simple qui renvoir une String.
Mon problème est le suivant:
- 1 fois sur 2 le service fonctionne bien et le reste du temps j’ai :
faultCode:Client.Authentication faultString:’An Authentication object was not found in the SecurityContext’ faultDetail:’null’
Et c’est vraiement une fois sur 2 quand je clique sur mon bouton. (un coup ca marche et le coup d’après ca ne marche pas, puis remarche,…)
De même en rafraîchissant la page j’ai des comportements de plus en plus bizarre
Archaon
11. nov, 2009
Bonjour,
En suivant pas par pas le tuto, je bloque sur la partie Java. FlexBuilder me signale une erreur sur la classe CandidateDAO au sein de laquelle l’utilisation de IGenericDAO pose problème.
IGenericDAO cannot be resolved to a type.
J’ai bien tous les packages requis dans votre tuto.
Merci
John
22. déc, 2009
Hello,
would you please upload us the source code !!
So that i could trace what you said in the tutorial in the source code..
Thanks
olivtt
13. jan, 2010
bonjour
excellent tuto, tout y est !
mais je me heurte à un souci en implémentant la sécurité
lorsque je démarre mon serveur tomcat (intégré à Eclipse)
j’obtiens
java.lang.ClassNotFoundException: javax.annotation.security.DenyAll
à priori c’est cette partie de config qui est en cause
si je la vire, je n’ai plus d’erreur mais la sécurité par les annotations ne fonctionne pas, bien-sûr
une idée ?
peut-être ma version du jdk (1.6.11) ou alors mon projet qui est en level compliance 1.5 ?
j’ai fait 2 ou 3 tests sans succès
merci de ta réponse
admin
04. fév, 2010
Cet article correspond en fait à des extraits de code existant (et non un tutoriel) ce qui explique la présence de classe non définie.
Mais dans le cas présent il doit être possible de compiler en supprimant le “extends IGenericDAO” dans la définition de la classe CandidateDAO
admin
04. fév, 2010
Pendant le développement j’ai également rencontré ce genre de comportement (peu rassurant à vrai dire). Mais après avoir effectué un clean, redémarrage de tomcat, il me semble que tout était ok.