Quand Spring s'invite dans une application Flex ...

le 23/04/2009 par David Rousselie
Tags: Software Engineering

Ces derniers temps, il ne se passe pas un mois sans que l'on entende parler d'un nouveau framework destiné au développement d'application Flex. Parmi ceux-ci, deux d'entre eux retiennent notre attention puisqu'ils sont hébergés au sein de Spring. Ces deux projets sont Spring Actionscript (en incubation pour le moment) et Spring Flex.

Spring Flex n'est qu'une extension au projet Spring que les développeurs Java connaissent bien. Il permet d'exposer des classes Java sous forme de services BlazeDS depuis la configuration Spring.

Spring ActionScript par contre, est un conteneur IoC destiné à la partie cliente d'une application Flex. C'est sur celui-ci que nous nous attarderons dans cet article.

Les principes de base de Spring Actionscript

À l'origine, Spring Actionscript était un framework nommé Prana Framework et ne faisait pas partie de Spring. Son auteur a débuté Prana en suivant les même principes que Spring Java afin qu'un développeur Java connaissant déjà Spring puisse s'y retrouver rapidement. C'est pourquoi le fichier de configuration ressemble très fortement à celui que l'on peut trouver en Java :

<objects>
	...
	<object id="remoteObject"
                class="mx.rpc.remoting.mxml.RemoteObject" abstract="true">
		<property name="endpoint"
                       value="http://localhost:8080/flexapp/messagebroker/amf" />
		<property name="showBusyCursor" value="true" />
	</object>

	<object id="myService" parent="remoteObject">
		<property name="destination" value="myService" />
	</object>
	...
</objects>

De même que pour la récupération d'un Object (là où Spring Java parle de Bean) :

applicationContext = new FlexXMLApplicationContext(["springas.xml"])
applicationContext.load()
...
var myObject: MyObject = applicationContext.getObject("myObject");
...

On retrouve donc la notion de contexte d'application depuis lequel on peut retrouver les objets déclarés dans le fichier de configuration. Une petite différence cependant est la méthode load() de l'object context. En effet, le fichier de configuration ("springas.xml" dans l'exemple) est téléchargé par l'application Flex sur le serveur (via HTTP donc) lors de l'appel à cette méthode.

Maintenant que nous avons vu que nous retrouvons les même principes de fonctionnement que Spring Java sur notre application Flex, quand utiliser l'injection de dépendance de SpringAS ?

Un premier cas d'utilisation est de déclarer tout ce qui concerne la configuration de l'application comme par exemple la déclaration des services (HTTPService, WebService ou RemoteObject). Ceci évite donc d'avoir à recompiler l'application lors d'un changement dans la configuration des services.

Un autre cas d'utilisation est lorsqu'une application Flex est architecturée avec des responsabilités séparées dans différentes classes ; typiquement avec une architecture MVC, MVP ou autre. Spring Actionscript supporte d'ailleurs, dans des "modules" séparés, Cairngorm et PureMVC. Ces deux frameworks MVC facilitent l'organisation du code d'une application Flex en séparant les responsabilités, mais le couplage entre les classes reste fort  dû à une utilisation abondante du pattern Singleton. SpringAS permet de les découpler en les liant seulement au travers d'interfaces et en injectant l'implémentation réelle d'après le fichier de configuration.

Intégration avec Spring Flex

Comme expliqué précédemment, les fichiers de configuration servant à initialiser le context SpringAS sont téléchargés sur le serveur via HTTP. Qui dit HTTP, dit URL mais pas forcément fichier. On peut ainsi générer ce fichier de configuration depuis une servlet (ou tout autre mécanisme de génération serveur) ?

Ça devient intéressant en utilisant Spring Flex. Celui-ci permet d'exposer une classe Java en tant que service Flex (au sens service BlazeDS et donc AMF/HTTP) juste à l'aide d'une déclaration dans le fichier de configuration Spring (Java cette fois-ci). Dans le fichier de configuration de SpringAS, on peut retrouver la déclaration des RemoteObjects, le pendant client de ces services Java. Pourquoi réécrire et maintenir cette configuration dans le fichier de configuration SpringAS alors que cela a déjà été fait dans la configuration Spring Java ? Une simple servlet Java peut ainsi récupérer la liste des classes Java exposées en tant que service Flex depuis la configuration Spring Java et générer la configuration SpringAS (ici la même configuration que le premier exemple de l'article) :

WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
String eol = System.getProperty("line.separator");

result.append("< ?xml version=\"1.0\" encoding=\"utf-8\" ?>").append(eol);
result.append("<objects>").append(eol);

URL requestURL = new URL(request.getRequestURL().toString());
result.append("<object id=\"remoteObject\" class=\"mx.rpc.remoting.mxml.RemoteObject\" abstract=\"true\">").append(eol);
result.append("<property name=\"endpoint\" value=\"http://").append(requestURL.getHost());
result.append(":").append(requestURL.getPort());
result.append(getServletContext().getContextPath());
result.append("/spring/messagebroker/amf\" />").append(eol);
result.append("<property name=\"showBusyCursor\" value=\"true\" />").append(eol);
result.append("</object>").append(eol);

String[] beanNames = context.getBeanNamesForType(FlexRemotingServiceExporter.class);
for (String beanName: beanNames) {
	result.append("<object id=\"").append(beanName).append("\" parent=\"remoteObject\">").append(eol);
	result.append("<property name=\"destination\" value=\"").append(beanName).append("\" />").append(eol);
	result.append("</object>").append(eol);
}
result.append("</objects>").append(eol);

response.getOutputStream().print(result.toString());

Conclusion

L'introduction de Spring sur la partie cliente d'une application peut être intéressante car elle apporte une certaine souplesse en séparant ce qui est de la configuration du reste du code, mais aussi en facilitant la testabilité de l'application en réduisant le couplage entre classes. Enfin, la génération de la configuration SpringAS sur le serveur permet d'imaginer des applications plus flexibles en "cablant" l'application dynamiquement (selon les droits de l'utilisateur par exemple).

Cependant, quelques problèmes persistent :

  • il n'est pas possible d'utiliser d'annotations pour injecter automatiquement les attributs d'une classe alors que Actionscript les supporte. Un début de solution existe mais n'est pas encore inclu dans SpringAS,
  • les classes qui ne sont pas utilisées (ie. qui n'ont pas de variables du type de la classe) par l'application principale mais seulement utilisées via le fichier de configuration ne seront pas inclues dans l'application à la compilation. Ceci est dû au fonctionnement du compilateur Flex qui n'inclut que les classes réellement utilisées. Or il n'a aucun moyen de déterminer que certaines classes seront utilisées après le chargement du fichier de configuration SpringAS. Pour contourner le problème il faut soit utiliser l'option "-includes" du compilateur et lui lister ces classes afin de le forcer à les inclure, soit déclarer des attributs fictifs (qui ne seront pas réellement utilisés) du type de ces classes dans l'application principale.

Enfin, même si SpringAS apporte de la souplesse à une architecture qui s'appuie sur Cairngorm, elle n'en diminue pas la complexité (il y a toujours autant, voir plus, de classes à créer). Un début de framework MVC semble en cours de développement au sein de SpringAS, peut-être nous apportera-t-il à la fois la simplicité et la souplesse. D'ici là, l'intérêt d'utiliser SpringAS pour une application Flex n'est pas donné et dépendra de chaque application et de son choix d'architecture.