Accueil > Non classé > Open-source Flex development stack

Open-source Flex development stack

Introduction

flexDans cet article, je vous propose de mettre en place un environnement de développement complet n’utilisant que des solutions Open Source (entièrement gratuit, donc totalement pas cher :)) pour faire du Flex. J’espère qu’il permettra aux personnes souhaitant démarrer un nouveau projet d’avoir une idée des différents outils existants, ainsi que de leur intégration dans un système d’informations déjà existant (au niveau du backend, notamment).

Les exemples qui vont être présentés dans la suite sont volontairement simplistes, le but n’est pas de faire la prochaine killer app, mais bien de voir comment mettre en place simplement et rapidement une “development stack” utilisant un frontend Flex et un backend Java ou PHP.

La plupart des technologies utilisées ici sont disponibles aussi bien sous Windows que Linux ou MacOS.

SDK Flex

La première chose à avoir pour faire du Flex, c’est bien évidemment un compilateur… Depuis février 2008, Adobe distribue un SDK Flex Open Source. La version actuellement stable est la 3.5. Flex 4 est en préparation (en beta2), donc nous le laisserons de côté pour l’instant. L’installation du SDK est simple : il suffit de télécharger une archive zip puis de l’extraire, par exemple, dans un répertoire flex-sdk-3.5.

Note : si comme moi vous êtes sur un OS 64bits avec un JRE 64bits, le compilateur mxmlc ne se lancera pas. Il nécessite un JRE 32bits, dont le chemin peut être configuré dans la propriété java.home du fichier bin/jvm.config du SDK. Il faut également que le répertoire bin du JRE soit dans le PATH.

IDE : FlashDevelop

Maintenant, il nous faut quelque chose à compiler ; pour éditer les fichiers sources, je vous propose un IDE Open Source nommé FlashDevelop. Cet éditeur gère des projets Flash, AS2, AS3 (dont AIR) et haXe (un langage de programmation à destination de plusieurs plateformes, dont la machine virtuelle Flash). flashdevelop Les fonctionnalités ne sont pas présentes en surnombre, mais le strict minimum est là :

  • gestion de projets ;
  • “Outline panel” montrant la structure des classes et des scripts MXML ;
  • complétion de code ;
  • intégration du compilateur fourni dans le SDK.

FlashDevelop est léger (l’installateur pèse moins de 4Mo) et stable (comparé à son principal concurrent commercial, Flex Builder). Comparé à un IDE Java tel que Netbeans ou IntelliJ (qui supporte des projets Flex aussi, mais pas dans sa version Open Source), les différences sont assez considérables, mais il ne faut pas oublier que Flex est une technologie relativement jeune, alors que Java a déjà 15 belles années derrière lui. “Inconvénients” majeurs : FlashDevelop ne fonctionne que sous Windows, et il ne permet pas de débugger les projets. Il n’existe pas à ma connaissance d’IDE Open Source fonctionnant également sous Linux ou MacOS.

Côté commercial, les solutions actuelles ne sont pas satisfaisantes non plus, de mon point de vue. Flex Builder et IntelliJ proposent tous les deux des fonctionnalités intéressantes, mais en les utilisant à longueur de journée on peut rapidement être lassés par des problèmes de consommation mémoire, des bugs étranges, des lenteurs…

Si vous êtes un true hacker, vous pouvez également vous en sortir avec un bon éditeur de texte avec coloration syntaxique, mais l’auto-complétion est toujours appréciable… De nombreux outils de ce type sont disponibles : Kate, jEdit, gedit etc. La gestion du projet et du build peuvent être “délégués” à Maven et son plugin flexmojos.

Après avoir téléchargé la version 3.0.6 RTM de FlashDevelop, vous pouvez l’installer selon la procédure standard Microsoftienne “suivant-suivant-installer-terminer”. Le SDK à utiliser peut être configuré dans “Tools -> Program Settings -> AS3Context -> Flex SDK location”.

Frontend Flex

Histoire de coder un peu, je vous propose de faire un “HelloWorld”, constitué d’un frontend Flex qui affichera la date et l’heure courantes en cliquant sur un bouton. Cette date sera récupérée via un service dans un backend. Je vous avais prévenus, c’est ultra-simpliste, mais c’est pour l’exemple. Créons donc un nouveau projet de type “Flex 3 project” dans FlashDevelop, nommé SimpleFlexApplication, avec comme package de base com.excilys.flex.sample. Changeons le contenu du fichier src/com/excilys/flex/sample/Main.mxml pour qu’il contienne :


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init()">
    <mx:Script>
        <![CDATA[
        import mx.controls.Alert;
        import mx.rpc.events.FaultEvent;
        import mx.rpc.events.ResultEvent;
        import mx.rpc.remoting.RemoteObject;
        import com.sample.service.Services;

        private var service:RemoteObject = Services.getDateService();

        private function init() : void {
            service.addEventListener(ResultEvent.RESULT, updateLabel);
            service.addEventListener(FaultEvent.FAULT, showError);
        }

        private function retrieveCurrentDate() : void {
            service.getCurrentDate();
        }

        private function updateLabel(event:ResultEvent) : void {
            dateLabel.text = event.message.body.toString();
        }

        private function showError(event:FaultEvent) : void {
            Alert.show("An error occured while trying to reach the backend.", "Oops!");
        }
        ]]>
    </mx:Script>
    <mx:Label id="dateLabel" text="(Click the button to retrieve the current date)"/>
    <mx:Button label="Retrieve current date" click="retrieveCurrentDate()" />
</mx:Application>

La vue contient donc un label et un bouton. Lors d’un clic sur le bouton, retrieveCurrentDate() appelle la méthode getCurrentDate() sur la variable service. Cet objet est de type RemoteObject, une classe de l’API Flex permettant d’appeler des services distants, en utilisant le format AMF (Action Message Format), basé sur SOAP et des RPC. Voici comment est construit cet objet, dans src/com/excilys/flex/sample/service/Services.as, une factory de RemoteObject :


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.excilys.flex.sample.service
{
    import flash.errors.IllegalOperationError;
    import mx.rpc.remoting.RemoteObject;

    /**
     * This class manages RemoteObjects used to call remote services.
     * @author Bastien
     */

    public class Services
    {
        public function Services()
        {
            throw new IllegalOperationError("This class is not meant to be instantiated!");
        }

        public static function getDateService() : RemoteObject
        {
            var service:RemoteObject = new RemoteObject();
            service.source = "DateService";
            service.destination = "amfphp";

            return service;
        }
    }
}

Après appel au constructeur, la source est spécifiée. Elle correspond au nom du service que l’on va appeler (généralement une classe du même nom se trouvant dans le backend). La destination permet grosso modo de spécifier de quelle manière accéder à ces services (entre autres quelle URL utiliser). Nous détaillerons cette partie dans le chapitre suivant.

Testons le frontend : lancez le projet en cliquant sur la “flèche bleue” dans la barre d’outils, ce qui va compiler le tout, générer un fichier SWF et l’exécuter. Une fois le frontend opérationnel, cliquez sur le bouton et vous serez gratifiés d’un joli message d’erreur :

epic-fail

Mais où est le backend ?

Normal, me direz-vous, puisqu’il manque la seconde moitié de l’application, à savoir… *roulements de tambour* … le backend (certes, elle était pas facile à deviner) ! Je vais vous présenter deux solutions possible pour le backend : l’une en PHP, la seconde en Java.

Backend PHP avec AMFPHP

AMFPHP est une implémentation Open Source du format AMF. Ce projet permet d’appeler des services écrits en PHP depuis la machine virtuelle Flash. Récupérez la dernière version et dézippez-la dans le répertoire www/ de votre serveur PHP favori (j’utilise WAMP). Après lecture de la doc, il suffit simplement d’ajouter nos services sous forme de classes (le nom du fichier étant celui de la classe) à placer dans le répertoire services/ de l’installation d’AMFPHP. Créons donc notre service de récupération, dans services/DateService.php :


1
2
3
4
5
6
7
<?php

class DateService {
    function getCurrentDate() {
        return strftime("%A %d %B %Y, %H:%M:%S");
    }
}

Deux choses sont à noter :

  • le nom DateService n’a pas été choisi au hasard, il correspond à la source du RemoteObject. Il est tout à fait possible de placer cette classe dans un sous-répertoire, dans ce cas la source aura la forme d’un package Java, par exemple “myservices.DateService” pour la classe “services/myservices/DateService.php” ;
  • le nom de la méthode appelée, getCurrentDate, n’est pas anodin non plus, c’est le même que la méthode invoquée sur le RemoteObject. Comme l’ActionScript est un langage dynamique, il est possible d’appeler des méthodes dont le nom n’est pas connu lors de la compilation.

Il reste maintenant à faire le lien entre le RemoteObject et notre backend AMFPHP. Pour cela, il faut utiliser le paramètre -services du compilateur mxmlc, prenant en paramètre un fichier de configuration XML dont voici le contenu (src/config.xml dans le projet Flex) :


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="UTF-8"?>
<services-config>
    <services>
        <service id="amfphp-flashremoting-service" class="flex.messaging.services.RemotingService"
                messageTypes="flex.messaging.messages.RemotingMessage">
            <destination id="amfphp">
                <channels>
                    <channel ref="my-amfphp"/>
                </channels>
                <properties>
                    <source>*</source>
                </properties>
            </destination>
        </service>
    </services>
    <channels>
        <channel-definition id="my-amfphp" class="mx.messaging.channels.AMFChannel" >
            <endpoint uri="http://localhost/amfphp/gateway.php"
                    class="flex.messaging.endpoints.AMFEndpoint" />
        </channel-definition>
    </channels>
</services-config>

Nous retrouvons la destination “amfphp” que nous avons utilisée plus haut sur le RemoteObject. Elle utilise le channel “my-amfphp” qui spécifie l’endpoint à utiliser (l’URL d’accès aux services). Il s’agit dans notre cas d’une gateway qui va appeler la bonne méthode sur la bonne classe, en fonction de l’invocation que nous faisons sur le RemoteObject. Pour utiliser ce fichier lors de la compilation, allez dans les propriétés du projet Flex pour ajouter un paramètre de compilation :

proprietes-projet

Fenêtre de configuration du projet FlashDevelop

Relancez l’application (et démarrez le serveur PHP si ce n’est pas déjà fait !), cliquez sur le bouton et admirez le résultat :

it-works

Les bloggeurs travaillent même le samedi...

Nous venons donc de faire communiquer Flex et PHP à peu de frais :

  • création d’un MXML et d’une classe ActionScript (pour la separation of concerns, la vue n’a pas à instancier des objets liés au “métier”)
  • création d’un service sous forme de classe PHP
  • création d’un fichier XML

Il est encore plus simple d’étendre ceci à d’autres services. On peut soit utiliser la même classe PHP, soit en ajouter d’autres. Côté Flex, on peut réutiliser le même RemoteObject et changer son attribut source (pratique dangereuse, faites attention aux accès concurrents), ou bien en instancier un par classe de services.

Intégration dans un projet PHP existant

Si vous disposez déjà d’une application PHP existante, utilisant par exemple le framework symfony (un article sans en parler aurait été étonnant de ma part :)), sachez qu’il est possible de facilement y intégrer AMFPHP, au moyen du plugin sfAmfPlugin. Le plus dur est certainement de déchiffrer l’anglais parfois approximatif de son auteur ;). Ce plugin permet de générer des classes de services (comme DateService) qui seront bien intégrées à symfony, par exemple pour faciliter l’accès aux DAO existants.

Backend Java + BlazeDS

Installation et configuration de BlazeDS

Essayons maintenant de nous connecter à des services écrits en Java. Il existe deux grands projets permettant le remoting AMF vers des services Java : BlazeDS et GraniteDS. Le premier est développé par Adobe, et a longtemps été “fermé” (étant intégré à LiveCycle), ce qui a motivé la création du second, un projet Open Source. Mais depuis décembre 2007, BlazeDS est également passé en Open Source.

Je vous propose de choisir BlazeDS, puisque j’ai déjà eu l’occasion de l’utiliser sur un autre projet. Nous allons l’utiliser dans un projet de webapp Maven 2, histoire de facilement télécharger nos dépendances. Je vais également utiliser Netbeans et GlassfishV3 pour déployer notre application. Après avoir créé un projet Maven avec l’archetype maven-archetype-webapp (Netbeans dispose d’un wizard pour générer ce genre de projets), ajoutez la dépendance suivante :


1
2
3
4
5
<dependency>
  <groupId>com.adobe.blazeds</groupId>
  <artifactId>blazeds-remoting</artifactId>
  <version>3.2.0.3978</version>
</dependency

Je ne détaillerai pas la mise en place de BlazeDS, je vais m’inspirer du fichier web.xml et du répertoire WEB-INF/flex/ présents dans l’exemple d’archive WAR téléchargeable sur le site d’Adobe (vous pouvez jeter un œil dans le code source de l’application à la fin de l’article pour plus d’infos). Basiquement, on met en place la classe flex.messaging.MessageBrokerServlet, mappée sur l’URL /messagebroker/*, qui va être l’endpoint contacté par le RemoteObject Flex. Cette servlet est configurée dans le fichier WEB-INF/flex/services-config.xml :


1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<services-config>
  <services>
    <service-include file-path="remoting-config.xml" />
  </services>
  <channels>
    <channel-definition id="my-amf">
      <endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/amf"/>
    </channel-definition>
  </channels>
</services-config>

L’url à contacter depuis Flex sera donc http://localhost:8080/SimpleBlazeDS/messagebroker/amf. Ajoutons maintenant une destination (pour atteindre notre service), dans le fichier WEB-INF/flex/remoting-config.xml :


1
2
3
4
5
6
<destination id="blazeds">
  <properties>
    <source>com.excilys.java.simpleblazeds.HelloService</source>
    <scope>application</scope>
  </properties>
</destination>

Le service HelloService se contente de nous saluer :


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.excilys.java.simpleblazeds;

/**
 * Simple service class that can be accessed by a Flex frontend.
 * @author Bastien
 */

public class HelloService {

  public String greet(String who) {
    if (who == null) {
      who = "world";
    }

    return "Hello, " + who + "!";
  }
}

Appel depuis le frontend

Il ne reste plus qu’à modifier le frontend pour ajouter un champ texte pour y entrer un nom, et un bouton qui va appeler la méthode greet() sur un autre RemoteObject en lui passant le nom entré :


1
2
3
4
5
6
7
8
private var helloService:RemoteObject = Services.getHelloService();

private function greet():void {
  helloService.greet(userName.text);
}

<mx:TextInput id="userName" text="Enter your name here"/>
<mx:Button label="greet" click="greet()"/>

Le résultat sera affiché dans une popup :

Enfin un HelloWorld :)

Enfin un HelloWorld :)

Le RemoteObject utilise la destination “blazeds” qu’il faudra ajouter, avec un second channel, dans src/config.xml du frontend. Cette destination doit porter le même nom que celle définie dans WEB-INF/flex/remoting-config.xml, sinon Flex se plaindra ! A ce propos, la destination par défaut d’AMFPHP était, vous l’aurez deviné, “amfphp”.

Conclusion

Vous voila maintenant officiellement développeur Flex (ou pas) ! Vous êtes capable de mettre en place, de manière rapide, un environnement Flex/PHP ou Flex/Java utilisant uniquement des outils Open Source (à l’exception de l’OS si vous utilisez FlashDevelop). Nous avons vu que dans les deux cas, la mise en place n’est pas très compliquée (il faut néanmoins faire un peu plus de configuration si l’on utilise BlazeDS, AMFPHP ne requiert que l’ajout de notre classe de service). Nous ne nous sommes intéressés qu’au remoting (appel de services par RPC), mais BlazeDS propose également :

  • un service de messaging, permettant à deux clients de s’envoyer des messages via le backend ;
  • un service de proxy, permettant à un client Flex de se connecter à n’importe quel autre serveur (par défaut, il ne peut se connecter à un serveur que si celui-ci l’accepte dans un fichier /crossdomain.xml).

Pour plus d’informations, vous pouvez consulter le guide de développement de BlazeDS.

Références

Code source du projet

Si vous souhaitez lire plus attentivement les sources ou la configuration des projets utilisés dans cet article, je vous invite à fouiner dans le code source du projet.

Share