Introduction▲
Dans ce tutoriel, nous allons mettre en place une application RIA composée d'un client riche Flex 3 s'exécutant au sein d'un navigateur web via le plugin Flash Player et d'un serveur d'application JEE exploitant Spring et BlazeDS.
Dans cette architecture, le client Flex réalise des appels de service s'exécutant au sein du serveur d'application JEE. Pour cela, j'ai décidé d'utiliser les appels de type RPC (Remote Procedure Call), et plus spécifiquement le composant RemoteObject de Flex, permettant d'invoquer des services Java, sans avoir à se soucier de la sérialisation/désérialisation des objets échangés entre Flex et Java.
BlazeDS est une brique open source, fournie par Adobe, supportant les appels RPC via RemoteObject. En pratique, il s'agit d'une servlet responsable, entre autres, de :
- localiser et invoquer les services Java ;
- désérialiser les données Flex reçues en instance d'objets Java et vice-versa.
BlazeDS s'interpose donc entre le client Flex et les services Java à la manière d'un proxy.
Enfin, nous intégrons ici le framework Spring, non par nécessité, mais parce que celui-ci est très populaire et que l'intégration entre Spring et BlazeDS est une problématique récurrente.
Dans notre architecture, Spring est utilisé comme container IoC. Spring gèrera donc le cycle de vie de nos services. BlazeDS devra s'adresser à Spring afin de localiser un service à invoquer.
Nous allons commencer par installer l'ensemble des outils nécessaires, puis nous mettrons en place un projet mixte Flex/WTP configuré pour l'utilisation de BlazeDS. Nous développerons un service Java et une IHM Flex très simple afin de valider notre travail.
Enfin, nous ajouterons Spring et adapterons la configuration de BlazeDS.
I. Récupération des produits logiciels▲
- Eclipse IDE for Java EE Developers – Windows
- Flex Builder 3 Professional Eclipse Plug-in - Windows (nécessite de s'enregistrer sur le site d'Adobe)
- BlazeDS : télécharger la dernière “Binary Distribution” depuis la page suivante : BlazeDS Releases
- Spring Framework : télécharger la dernière version stable : Spring Download
- Serveur d'application Tomcat 6.0. Prendre l'installation « core » sous forme d'archive zip : Apache Tomcat 6.0.x
II. Préparation de l'environnement de développement▲
Nous allons installer Eclipse, le plugin Flex Builder 3 et le serveur d'application Apache Tomcat, puis les framework BlazeDS et Spring.
Pour les besoins de l'article, l'ensemble sera installé dans le répertoire c:\appl.
II-A. Prérequis▲
Vous devez disposer d'un JDK 1.5 ou plus d'installé. Pour vérifier, ouvrir un invité de commande et taper :
C
:
\>
java -
version
java version "1.5.0_08"
Java
(
TM) 2
Runtime Environment, Standard Edition (
build 1.5.0_08
-
b03)
Java HotSpot
(
TM) Client VM (
build 1.5.0_08
-
b03, mixed mode)
Se reporter à la FAQ Java en cas de problème : La FAQ Java
II-B. Installations▲
Décompresser l'archive d'Eclipse dans le répertoire c:\appl\eclipse. Pour la suite de cet article, nous considérerons qu'Eclipse est installé dans ce répertoire.
Une fois Eclipse installé, lancer l'installation du plugin Flex Builder 3 Professional.
Ceci nécessite les droits d'administration sur le poste de travail.
Choisir c:\appl\fb3 comme répertoire d'installation, et c:\appl\eclipse comme répertoire où est installé Eclipse :
Décompresser l'archive de Tomcat 6.0 dans c:\appl\tomcat, celle de Spring dans c:\appl\spring et enfin celle de BlazeDS dans c:\appl\blazeds.
Vous trouverez, dans c:\appl\blazeds, deux fichiers, dont blazeds.war qui contient une webapp JEE préconfigurée pour BlazeDS.
III. Création et configuration du projet▲
- Démarrer Eclipse.
- Sélectionner un répertoire pour la création d'un workspace. Par ex : c:\worspace.
- Ouvrir la perspective Flex Development (Menu Windows / Open Perspective / Other… / Flex Development)
- Créer un nouveau projet Flex (Menu File / New / Flex Project).
- project name : FBSIntegration pour Flex BlazeDS Spring Integration.
- application server type : J2EE.
- laisser coché “LiveCycle Data Services” et Create combined Java/Flex project using WTP.
- Cliquer sur le bouton Next afin d'atteindre l'étape Configure JEE Server.
- Cliquer sur le bouton New… afin de configurer une instance du serveur Tomcat dans Eclipse.
- Sélectionner Apache / Tomcat v6.0 Server, puis cliquer sur Next>
- Renseigner le champ “Tomcat installation directory” pour qu'il pointe sur le répertoire d'installation de Tomcat (c:\appl\tomcat). Puis cliquer sur Finish.
- De retour au wizard de configuration du projet Flex/WTP, renseigner le champ Flex WAR File pour désigner le fichier blazeds.war situé dans le répertoire d'installation de BlazeDS (c:\appl\blazeds\blazeds.war).
- Cliquer sur Next >.
- L'écran suivant nous indique que les fichiers source Flex sont situés dans flex_src/, tandis que les fichiers source Java sont situés dans src/.
- Cliquer sur Finish.
Il reste un dernier paramétrage à réaliser :
- sélectionner le projet FBSIntegration dans la vue Flex Navigator, puis afficher les propriétés du projet (menu Project / Properties / Flex Server) ;
- modifier le champ Context root pour que celui-ci désigne correctement le nom du contexte de notre application, à savoir /FBSIntegration.
Enfin, il faut configurer le serveur Tomcat au sein d'Eclipse pour qu'il déploie notre application :
- si ce n'est pas déjà fait, afficher la vue Servers (menu Windows / Show View / Other / Server / Servers) ;
- ajouter le projet FBSIntegration au serveur Tomcat (Bouton droit sur le serveur “Tomcat v6.0 server at localhost, et ajouter le projet FBSIntegration à la liste des Configured Projects (menu Add and Remove Projects…).
IV. Première application▲
Nous allons développer une application très simple, de type « Hello World » afin de valider le bon fonctionnement de l'ensemble.
Le scénario est le suivant.
- L'utilisateur saisit un login, et clique sur le bouton « Appel de WelcomeService ».
- Ceci déclenche un appel à la méthode sayHello du service distant welcomeService.
- Le message retourné par la méthode sayHello est affiché dans la zone de réponse.
- En cas d'échec de l'appel, le message d'erreur est affiché dans la zone de réponse.
IV-A. Développement de l'IHM Flex▲
Le fichier flex_src/FBSIntegration.mxml contient principalement :
- le champ de saisie du login : tiLogin ;
- le bouton déclenchant l'appel : btnAppel ;
- le composant RemoteObject permettant d'effectuer l'appel distant : roWelcomService ;
- la zone de texte permettant d'afficher le résultat de l'appel : taReponse.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" width="451" height="168"
paddingBottom="0" paddingLeft="0" paddingRight="0" paddingTop="0">
<mx:Script>
<![CDATA[
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
private function doCall():void{
// Appeler le service distant
roWelcomeService.sayHello(tiLogin.text);
}
/**
* Méthode invoquée en cas de succès de l'appel RPC
*/
private function onResult(event : ResultEvent):void{
// Afficher la réponse
taReponse.text = event.result as String;
}
/**
* Méthode invoquée en cas d'échec de l'appel RPC
*/
private function onFault(event : FaultEvent):void{
// Afficher le message d'erreur
taReponse.text = event.fault.message;
}
]]>
</mx:Script>
<mx:RemoteObject id="roWelcomeService"
destination="welcomeServiceDest"
result="onResult(event)"
fault="onFault(event)"/>
<mx:Panel width="451" height="168" layout="absolute" title="Hello World">
<mx:Label x="10" y="10" text="Login : "/>
<mx:TextInput id="tiLogin" text="Guest"
x="66" y="8" />
<mx:Button id="btnAppel" label="Appel de WelcomeService"
x="234" y="8"
click="doCall()"/>
<mx:Label x="10" y="38" text="Réponse :"/>
<mx:TextArea id="taReponse"
x="81" y="37" width="327" height="81"/>
</mx:Panel>
</mx:Application>
IV-B. Premier test▲
Nous allons tester l'application en l'état. Pour cela, il faut :
- démarrer le serveur Tomcat ;
- ouvrir un navigateur sur l'URL suivante : http://localhost:8080/FBSIntegration/FBSIntegration.swf ;
- cliquer sur le bouton « Appel de WelcomeService »
Bien entendu, l'appel échoue, car nous n'avons pas encore développé le service « WelcomeService » appelé par l'IHM Flex, ni déclaré celui-ci auprès de BlazeDS !
IV-C. Création de WelcomeService▲
Dans le répertoire src/, créer la classe WelcomeService dans le package com.developpez.service :
package
com.developpez.service;
public
class
WelcomeService {
public
String sayHello
(
String pLogin){
return
"Bienvenue "
+
pLogin;
}
}
Ce service très simple définit la méthode sayHello, qui prend en argument un login et retourne un message de bienvenue incluant le login fourni.
IV-D. Déclaration du service auprès de BlazeDS▲
Une fois le service Java créé, il faut configurer BlazeDS pour que celui-ci sache le localiser et l'associe à une « destination ».
- Éditer le fichier remoting-service.xml, situé dans WebContent/WEB-INF/flex/.
- Il faut y ajouter la destination welcomeServiceDest :
<?xml version="1.0" encoding="UTF-8"?>
<service
id
=
"remoting-service"
class
=
"flex.messaging.services.RemotingService"
>
<adapters>
<adapter-definition
id
=
"java-object"
class
=
"flex.messaging.services.remoting.adapters.JavaAdapter"
default
=
"true"
/>
</adapters>
<default-channels>
<channel
ref
=
"my-amf"
/>
</default-channels>
<destination
id
=
"welcomeServiceDest"
>
<properties>
<source>
com.developpez.service.WelcomeService</source>
<scope>
application</scope>
</properties>
</destination>
</service>
Flex Builder ne recompile pas l'application Flex suite à la modification de ce fichier !
Il faut donc soit faire un Project / clean, soit légèrement modifier le fichier mxml pour que celui-ci soit recompilé et intègre la déclaration de la destination.
IV-E. Tester l'application▲
Retestons l'application afin de s'assurer que nous avons correctement configuré BlazeDS (se reporter à la section IV-b).
Cette fois, l'appel au service réussit et nous retourne le message de bienvenue ! :-)
V. Intégration de Spring▲
Nous allons intégrer le framework Spring dans notre environnement, en suivant les étapes suivantes :
- ajout des dépendances au projet ;
- initialisation du context Spring ;
- déclaration de WelcomeService dans le context Spring ;
- configuration de BlazeDS.
V-A. Ajout des dépendances au projet▲
Copier le fichier spring.jar, situé dans c:\appl\spring\dist dans le répertoire WebContent/WEB-INF/lib du projet, et rafraîchir le projet si nécessaire.
La bibliothèque spring.jar doit apparaître dans les Web App bibliothèques de la section Java Build Path des propriétés du projet :
V-B. Configuration du contexte Spring▲
Pour une introduction à Spring, vous pouvez consulter ce tutoriel : Introduction au framework Spring (par Erik Gollot).
Nous allons tout d'abord ajouter le fichier webApplicationContext.xml de configuration du contexte Spring puis modifier le fichier web.xml afin d'initialiser ce contexte.
Ajouter le fichier webApplicationContext.xml suivant à la racine du répertoire src/ :
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns
=
"http://www.springframework.org/schema/beans"
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-2.0.xsd"
>
</beans>
Puis il faut modifier le fichier web.xml situé dans WebContent/WEB-INF.
Nous y ajoutons une section « context-param » et un « listener », comme suit :
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
<display-name>
FBSIntegration</display-name>
<description>
BlazeDS Application</description>
<context-param>
<param-name>
flex.class.path</param-name>
<param-value>
/WEB-INF/flex/hotfixes,/WEB-INF/flex/jars</param-value>
</context-param>
<!-- Chemin d'acces au fichier de configuration Spring -->
<context-param>
<param-name>
contextConfigLocation</param-name>
<param-value>
/WEB-INF/classes/webApplicationContext.xml</param-value>
</context-param>
<!-- Spring listener -->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
...
</web-app>
Nous allons déclarer le bean welcomeServiceBean en tant qu'instance de la classe WelcomeService, en ajoutant une déclaration au fichier webApplicationContext.xml précédemment créé :
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns
=
"http://www.springframework.org/schema/beans"
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-2.0.xsd"
>
<bean
id
=
"welcomeServiceBean"
class
=
"com.developpez.service.WelcomeService"
/>
</beans>
V-C. Intégration de Spring à BlazeDS▲
En l'état, BlazeDS n'utilise pas le context Spring pour localiser les instances du service.
Il est nécessaire de configurer, auprès de BlazeDS, une Factory spécifique, qui s'occupera d'interroger le contexte Spring.
Cette technique a été initialement proposée par Jeff Vroom : Spring and Flex Integration by Jeff Vroom.
Il vous faut ajouter le fichier SpringFactory.java suivant dans le package com.developpez.web :
package
com.developpez.web;
import
org.springframework.beans.BeansException;
import
org.springframework.beans.factory.NoSuchBeanDefinitionException;
import
org.springframework.context.ApplicationContext;
import
org.springframework.web.context.support.WebApplicationContextUtils;
import
flex.messaging.FactoryInstance;
import
flex.messaging.FlexFactory;
import
flex.messaging.config.ConfigMap;
import
flex.messaging.services.ServiceException;
/**
* This interface is implemented by factory components which provide
* instances to the flex messaging framework. To configure flex data services
* to use this factory, add the following lines to your services-config.xml
* file (located in the WEB-INF/flex directory of your web application).
*
* <factories>
* <factory id="spring" class="flex.samples.factories.SpringFactory" />
* </factories>
*
* You also must configure the web application to use spring and must copy the spring.jar
* file into your WEB-INF/lib directory. To configure your app server to use spring,
* you add the following lines to your WEB-INF/web.xml file:
*
* <context-param>
* <param-name>contextConfigLocation</param-name>
* <param-value>/WEB-INF/applicationContext.xml</param-value>
* </context-param>
*
* <listener>
* <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
* </listener>
*
* Then you put your spring bean configuration in WEB-INF/applicationContext.xml (as per the
* line above). For example:
*
* <?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="weatherBean" class="dev.weather.WeatherService" singleton="true"/>
* </beans>
*
* Now you are ready to define a destination in flex that maps to this existing service.
* To do this you'd add this to your WEB-INF/flex/remoting-config.xml:
*
* <destination id="WeatherService">
* <properties>
* <factory>spring</factory>
* <source>weatherBean</source>
* </properties>
* </destination>
*
*
@author
Jeff Vroom
*/
public
class
SpringFactory implements
FlexFactory
{
private
static
final
String SOURCE =
"source"
;
/**
* This method can be used to initialize the factory itself. It is called with configuration
* parameters from the factory tag which defines the id of the factory.
*/
public
void
initialize
(
String id, ConfigMap configMap) {}
/**
* This method is called when we initialize the definition of an instance
* which will be looked up by this factory. It should validate that
* the properties supplied are valid to define an instance.
* Any valid properties used for this configuration must be accessed to
* avoid warnings about unused configuration elements. If your factory
* is only used for application scoped components, this method can simply
* return a factory instance which delegates the creation of the component
* to the FactoryInstance's lookup method.
*/
public
FactoryInstance createFactoryInstance
(
String id, ConfigMap properties)
{
SpringFactoryInstance instance =
new
SpringFactoryInstance
(
this
, id, properties);
instance.setSource
(
properties.getPropertyAsString
(
SOURCE, instance.getId
(
)));
return
instance;
}
// end method createFactoryInstance()
/**
* Returns the instance specified by the source
* and properties arguments. For the factory, this may mean
* constructing a new instance, optionally registering it in some other
* name space such as the session or JNDI, and then returning it
* or it may mean creating a new instance and returning it.
* This method is called for each request to operate on the
* given item by the system so it should be relatively efficient.
* <p>
* If your factory does not support the scope property, it
* report an error if scope is supplied in the properties
* for this instance.
*/
public
Object lookup
(
FactoryInstance inst)
{
SpringFactoryInstance factoryInstance =
(
SpringFactoryInstance) inst;
return
factoryInstance.lookup
(
);
}
static
class
SpringFactoryInstance extends
FactoryInstance
{
SpringFactoryInstance
(
SpringFactory factory, String id, ConfigMap properties)
{
super
(
factory, id, properties);
}
public
String toString
(
)
{
return
"SpringFactory instance for id="
+
getId
(
) +
" source="
+
getSource
(
) +
" scope="
+
getScope
(
);
}
public
Object lookup
(
)
{
ApplicationContext appContext =
WebApplicationContextUtils.getWebApplicationContext
(
flex.messaging.FlexContext.getServletConfig
(
).getServletContext
(
));
String beanName =
getSource
(
);
try
{
return
appContext.getBean
(
beanName);
}
catch
(
NoSuchBeanDefinitionException nexc)
{
ServiceException e =
new
ServiceException
(
);
String msg =
"Spring service named '"
+
beanName +
"' does not exist."
;
e.setMessage
(
msg);
e.setRootCause
(
nexc);
e.setDetails
(
msg);
e.setCode
(
"Server.Processing"
);
throw
e;
}
catch
(
BeansException bexc)
{
ServiceException e =
new
ServiceException
(
);
String msg =
"Unable to create Spring service named '"
+
beanName +
"' "
;
e.setMessage
(
msg);
e.setRootCause
(
bexc);
e.setDetails
(
msg);
e.setCode
(
"Server.Processing"
);
throw
e;
}
}
}
}
Une fois la SpringFactory créée, il est nécessaire de la déclarer pour que BlazeDS sache la localiser.
Pour cela, ajouter une section <factories> au début du fichier services-config.xml, situé dans WebContent/WEB-INF/flex :
<?xml version="1.0" encoding="UTF-8"?>
<services-config>
<factories>
<factory
id
=
"SpringFactory"
class
=
"com.developpez.web.SpringFactory"
/>
</factories>
...
</services-config>
Enfin, il faut modifier la destination welcomeServiceDest dans le fichier remoting-config.xml, pour que celle-ci utilise la factory SpringFactory afin de localiser le bean Spring welcomeServiceBean.
La balise <factory> est ajoutée, afin de spécifier la SpringFactory déclarée dans le services-config.xml.
La balise <source> est modifiée afin de spécifier le nom du bean Spring à utiliser.
Enfin, la balise <scope> est supprimée, car c'est Spring qui gère dorénavant le cycle de vie du service.
<destination
id
=
"welcomeServiceDest"
>
<properties>
<factory>
SpringFactory</factory>
<source>
welcomeServiceBean</source>
</properties>
</destination>
V-D. Tester l'application finale▲
Démarrer le serveur d'application Tomcat
Dans la console, les traces suivantes permettent de vérifier l'initialisation correcte de Spring :
INFO: Initializing Spring root WebApplicationContext
...…
INFO: Loading XML bean definitions from ServletContext resource [/WEB-INF/classes/webApplicationContext.xml]
...
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@7f58ef: defining beans [welcomeServiceBean]; root of factory hierarchy
...
INFO: Root WebApplicationContext: initialization completed in 242 ms
Lancer à nouveau un navigateur sur l'URL suivante : http://localhost:8080/FBSIntegration/FBSIntegration.swf
VI. Pour aller plus loin…▲
Vous disposez maintenant d'un projet mixte WTP/Flex correctement configuré pour exploiter Flex, BlazeDS et Spring. À vous maintenant de construire sur cette base votre application !
Voici quelques ressources utiles :