Singleton, chargement de multiples applications Flex (SWF) et loaderContext
98% du temps, je vous dirais que la création de Singletons dans votre application est une mauvaise pratique, à éviter (par expérience). Bon, il reste 2%, tout premièrement car le framework Flex utilise lui-même des Singletons (chaque application est un singleton, mais aussi PopUpManager, DragManager, CursorManager & co). Et parfois, ils peuvent vous sauver la mise pour éviter de mettre en branle des techniques de sioux pour arriver à ses fins.
C'est ce qui m'est arrivé hier pendant mes développements dans une situation assez spéciale que l'on ne rencontre pas tous les jours.
Le contexte.
Je développe actuellement une administration pour un SIG Web qui consiste en une série d'applications Flex. Ces applications sont indépendantes dans leur fonctionnement et sont chargées par une application-mère qui consiste simplement en un TabNavigator contenant des SWFLoader.
Un petit schéma pour expliquer la situation:
La contrainte
Ces applications communiquent toutes avec un seul et même serveur baptisé Aigle. Ce serveur ne peut, par utilisateur, ne recevoir qu'une seule requête à la fois et c'est ici la contrainte qui nous intéresse. Si le serveur reçoit 2 requêtes du même utilisateur, la 2 sera mise en attente pendant un certain temps. Si la sous-application 1 fait une requête assez longue et que l'on bascule sur un autre onglet pour faire quelque chose qui va envoyer un autre requête, on tombera donc sur ce problème.
Le système d'envoi de requêtes en 2 mots
Pour envoyer des requêtes au serveur, j'utilise un Singleton (oui, c'est le dernier) nommé "RequestManager". Celui-ci s'occupe d'envoyer les requêtes au serveur à la chaine. Quand on tente d'envoyer une requête, on va simplement alimenter une pile de requête qui se dépile lorsque l'on reçoit le retour d'une requête. On a donc une FIFO (si je ne m'abuse) qui me permet de ne pas me heurter à la contrainte imposée par le serveur.
La classe RequestManager se trouve dans une librairie, utilisée par tous les projets.
Première approche, chargement simple des SWF applicatifs avec SWFLoader
Dans un premier temps, j'ai tenté de simplement créer des new SWFLoader et de faire un load(pluginPath.swf). Dans ce cas-là, chaque SWF est dans une "sandbox" locale. Il n'y a aucun partage de classe, chaque SWF est une entité bien distincte. Si on charge 6 sous-applications, on aura donc 6 instances bien distinctes de RequestManager (ce sont des Singletons par rapport à chaque SWF).
Flex Modules – TypeError: Error #1034: Echec de la contrainte de type : conversion de mx.managers::PopUpManagerImpl@18658d01 en mx.managers.IPopUpManager impossible [Résolu]
Voici un bug Flex que vous allez sûrement rencontrer si votre application comporte plusieurs modules à la fois. Ce n'est pas vraiment un bug en soi, c'est simplement un comportement du Framework Flex qu'il faut connaître pour éviter les surprises.
Le bug en question
Ce bug se produit de nombreuses manières différentes, mais toujours quand vous allez utiliser plusieurs modules dans une même application. Après des actions qui peuvent pourtant sembler banales, vous allez obtenir une RTE (RunTime Error) assez étrange du style:
TypeError: Error #1034: Echec de la contrainte de type : conversion de mx.managers::PopUpManagerImpl@18658d01 en mx.managers.IPopUpManager impossible. at mx.managers::PopUpManager$/get impl()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\managers\PopUpManager.as:68] at mx.managers::PopUpManager$/addPopUp()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\managers\PopUpManager.as:169] at mx.controls::ComboBox/getDropdown()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\controls\ComboBox.as:1459] at mx.controls::ComboBox/displayDropdown()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\controls\ComboBox.as:1552] at mx.controls::ComboBox/downArrowButton_buttonDownHandler()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\controls\ComboBox.as:1801] at flash.events::EventDispatcher/dispatchEventFunction() at flash.events::EventDispatcher/dispatchEvent() at mx.core::UIComponent/dispatchEvent()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\core\UIComponent.as:9298] at mx.controls::Button/http://www.adobe.com/2006/flex/mx/internal::buttonPressed()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\controls\Button.as:2504] at mx.controls::Button/mouseDownHandler()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\controls\Button.as:2750] at flash.events::EventDispatcher/dispatchEventFunction() at flash.events::EventDispatcher/dispatchEvent() at mx.core::UIComponent/dispatchEvent()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\core\UIComponent.as:9298] at mx.controls::ComboBase/textInput_mouseEventHandler()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\controls\ComboBase.as:1388]
Cette erreur de conversion (Coercion) se reproduit facilement sur le PopUpManager ou sur le DragManager mais peut aussi se produire avec vos propres classes. Vous obtiendrez des messages qui pourront vous sembler étranges. Par exemple:
TypeError: Error #1034: Type Coercion failed: cannot convert MyClass@5d73ce1 to MyClass.
Une conversion de type entre un type MyClass et le même type MyClass qui ne fonctionne pas, il y a de quoi se poser des questions. Ce bug a été maintes fois reporté sur la Bug Base d'Adobe (SDK-16474, SDK-14384, …) mais peut facilement être corrigé.
Un exemple d'application qui pose problème
Flex Shared Object – Exemple d'utilisation des SharedObject (authentification)
Dans ce tutorial Flex, nous allons construire une application simple qui utilise les SharedObject de manière pratique. Cet exemple affiche une formulaire de login (authentification login/mot de passe). Cependant, l'utilisateur a aussi la possibilité de fixer une préférence pour que l'application se souvienne de lui.
Cette application utilise un composant MXML qui affiche une fenêtre de login. Elle utilise aussi une classe Singleton User qui permet à l'utilisateur de s'identifier. Notez que dans cet exemple, l'application utilise des valeurs codées en dur pour l'authentification. Dans une vraie application, l'authentification se ferait grâce à des données contenues dans une base de données, LDAP, ou d'autres.






