Interopérabilité REST : Accès à une logique métier Java depuis un client Silverlight
Article écrit le 4 mars 2009 par Stève Sfartz, Architecte Système d'Information
Téléchargez le code-source
Sommaire
Introduction
Conception de l’API Rest
Implémentation au travers du framework Restlest
Test de nos services rest
Appel des services web depuis silverlight
Constitution de l’interface Silverlight
Exécution des codes accompagnant cet article
Ressources
Introduction
La technologie Silverlight 2 permet d’intégrer des données dynamiquement grâce à l’appel de Services Web de communication de type REST et SOAP. Au cours de cet article, nous illustrerons un scénario d’interopérabilité où une application Web Riche Silverlight fera appel à des services Web REST, développés à l’aide du framework opensource Java Restlet. Dans un second temps, nous présenterons comment exécuter le projet dans l’environnement Eclipse grâce à l’extension Eclipse Tools for Silverlight.
.jpg)
Figure 1 : Copie Ecran de l’application Silverlight illustrée dans cet article
Conception de l’API REST
Tout d’abord, attachons nous à développer la logique métier. Dans le cadre de notre exemple, il s’agit de proposer une liste de clients, d’accéder aux informations détaillées d’un client et de proposer à la mise à jour ou la suppression d’une fiche client. De plus, il faudra être capable de filtrer la liste en fonction du pays sélectionné.
Une conception REST de l’application nous amène naturellement à isoler la ressource Client. Les URI suivantes seront proposées :
URI | HTTP | Description |
/customers/ | GET | Retourne la liste des références vers des clients |
/customers/{id} | GET | Retourne le client dont l’ID est spécifié |
/customers/ ?country=France | GET | Retourne la liste des clients résidents en France |
/customers/{id} | PUT | Modifie le client avec les valeurs spécifiées via le flux XML d’entrée |
/customers/{id} | DELETE | Supprime le client |
/customers/ | POST | Crée un nouveau client avec les valeurs spécifiées via le flux XML d’entrée |
/customers/?expand=true | GET | Retourne la liste des références vers les clients ainsi que les propriétés pour chaque client |
Figure 2 : Conception des URI : deux ressources « Client » et « Liste de clients »
Remarque : par soucis d’optimisation, nous avons ajouté une URI comprenant l’extension « expand ». Cette URI permet de développer le graphe de clients à partir de l’accès à la liste complète des clients, ainsi nous retournerons le flux en figure X depuis l’invocation de l’URL … plutôt que le flux situé en figure Y. En effet, ce dernier nécessiterait d’être développé par des requêtes complémentaires (1 accès plutôt que N+1 accès où N est égal au nombre de clients affichés par l’interface Silverlight).
Implémentation au travers du framework Restlet
Le framework Restlet pour Java intègre nativement les concepts REST au niveau de son architecture. Ainsi, l’implémentation du scénario présenté précédemment se traduit par la création des ressources Client (« Customer ») et Liste de Clients (« Customers »).
Pour chacune de ces ressources, nous exposerons des représentations au format XML, et nous autoriserons la soumission de flux XML. Pour ce faire, nous déclarons supporter le MediaType TEXT_XML, et nous déclarons les ressources modifiables, c’est-à-dire, nous serons invoqués lors des méthodes HTTP POST, PUT et DELETE. Pour la génération du flux XML, nous avons choisi d’utiliser l’API DOM de Java. Ci-après un code source permettant de tenir compte d’une modification client.
public void storeRepresentation(Representation entity)
throws ResourceException
{
DomRepresentation dr = new DomRepresentation(entity);
Node node = dr.getNode("//Customer/CustomerId");
String id = node.getTextContent();
node = dr.getNode("//Customer/ContactName");
String name = node.getTextContent();
node = dr.getNode("//Customer/Country");
String country = node.getTextContent();
node = dr.getNode("//Customer/Company");
String company = node.getTextContent();
// Now, let's update the customer properties
getCustomer().setID(id);
getCustomer().setName(name);
getCustomer().setCountry(country);
getCustomer().setCompany(company);
}
Remarque : au niveau du code source Java qui accompagne cet article, vous remarquerez que nous avons choisi de maintenir la liste des clients en mémoire de façon à simplifier le code.
Il nous reste maintenant à exposer ces ressources au travers d’URI. Le framework Restlet permet de réaliser ces mappings au travers d’attachements, sachant que le moteur d’exécution peut être soit un serveur Web (Tomcat notamment, l’activation ayant lieu grâce à une Servlet), soit un conteneur léger proposé dans le framework (qui peut aussi faire office de serveur Web). Dans l’exemple joint à cet article, nous avons opté pour le conteneur léger (mode « self hosting »).
public synchronized Restlet createRoot() {
// Create a router Restlet that defines routes.
Router router = new Router(getContext());
// Defines a route for the resource List of Customers
router.attach("/customers/", CustomersResource.class);
// Defines a route for the resource List of Customers
router.attach("/customers/{id}", CustomerResource.class);
return router;
}
Test de nos Services REST
Nos services sont désormais opérationnels. Nous pouvons les tester au travers de l’invocation des URI depuis un browser Web pour les méthodes GET. Pour télécharger et exécuter les codes accompagnant cet exemple, reportez vous à la section « Exécution » de l’article.
Pour ce qui est des ordres POST, PUT et DELETE, nous devons utiliser un outil qui soumettra nos flux XML. L’outil Fiddler fait référence dans ce domaine, il est téléchargeable à cette adresse : http://www.fiddlertool.com/fiddler/.
Par défaut, Fiddler intercepte les flux HTTP en se positionnant comme proxy. Cette fonctionnalité nécessite d’être paramétrée pour certains navigateurs et frameworks.
.jpg)
Figure 3 : Visualisation d’une méthode GET avec Fiddler
.jpg)
Figure 4 : Soumission d’un flux XML pour créer une ressource
Remarque : il est possible de faire un drag & drop d’une requête interceptée par Fiddler depuis la liste située sur la gauche vers l’onglet « Request Buidler ».
Appel des Services Web depuis Silverlight
Afin d’invoquer les Services REST créés précédemment, Silverlight propose la classe WebClient qui permet d’invoquer une URI et récupérer le flux sortant. Ces invocations sont asynchrones de sorte à ce que le rendu graphique soit indépendant des temps de latence réseau. Ainsi un code d’invocation d’un Service REST peut être décrit comme suit :
- Préparation de la requête, affectation du handler asynchrone
- Invocation du service REST
- Récupération du résultat
Le code attaché à cet article propose un pattern d’implémentation qui permet de regrouper l’ensemble du code invocation / traitement du résultat au sein d’un segment de code bien identifiable. Ce pattern est intéressant pour les aspects maintenance.
public void run()
{
// Disable Button
container.load.IsEnabled = false;
// Build URL with Filter
string requestURI = "http://localhost:8182/customers/?expand=true";
string filter = container.country.Text;
if (!String.IsNullOrEmpty(filter))
{
requestURI += "&country=" + filter;
}
var client = new WebClient();
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler (client_DownloadStringCompleted);
client.DownloadStringAsync(newUri(requestURI));
}
void client_DownloadStringCompleted(object s, DownloadStringCompletedEventArgs e)
{
string reponse = e.Result;
IEnumerable<Customer> customers = null;
if (e.Error == null)
{
XDocument doc = XDocument.Parse(reponse);
customers = from customer in
doc.Elements("Customers").Elements("Customer")
select new Customer
{
CustomerId = customer.Element("CustomerId").Value.ToString(),
ContactName = customer.Element("ContactName").Value.ToString(),
Country = customer.Element("Country").Value.ToString(),
Company = customer.Element("Company").Value.ToString()
};
}
container.list.ItemsSource = customers;
// Enable Button
container.load.IsEnabled = true;
}
Pour finaliser notre exemple, il nous faut néanmoins prendre en compte deux importantes limitations de la technologie Silverlight, qui se voit imposée les contraintes inhérentes aux plugins des navigateurs Web. Si vous souhaitez investiguer ces contraintes, nous avons détaillé ces aspects lors de l’évènement « Rendez-vous de l’interopérabilité Silverlight » figurant dans la section Ressources.
Limitation 1 : Invocations HTTP restreintes aux méthodes GET et POST.
Cette limitation est suffisamment répandue pour que le framework Restlet l’ait prise en compte et intègre par défaut la possibilité de surcharger une méthode POST au travers du paramètre d’URL « ?method=<PUT|DELETE> ».
Limitation 2 : Politique d’invocation Cross Domain
Alors que notre application Silverlight est mise à disposition au travers d’un site Web accessible à partir de l’adresse http://localhost:8183. Pour pouvoir être consommés en Silverlight, il est nécessaire de configurer la sécurité d’accès par le client Silverlight aux Services REST. Ceci est obligatoire lorsque les services accédés ne sont pas situés sur le site d’origine de l’application Silverlight. Dans ce cas, il est nécessaire de déclarer une politique d’accès cross-domain qui définit des autorisations d’accès. Nous avons donc déclaré au niveau du projet Java une ressource Restlet supplémentaire correspondant à la politique de sécurité de Silverlight « clientaccesspolicy.xml ». Pour plus d’informations sur ce sujet, consulter ce billet …
// Defines a route for the client access policy
// so that Services can be accessed from any silverlight application
router.attach("/clientaccesspolicy.xml", ClientAccessPolicyResource.class);
public class ClientAccessPolicyResourceextends Resource {
public ClientAccessPolicyResource(Context context, Request request,
Response response) {
super(context, request, response);
// TODO Auto-generated constructor stub
getVariants().add(new Variant(MediaType.TEXT_XML));
}
publicRepresentation represent(Variant variant) {
StringBuilder builder = new StringBuilder();
builder.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
builder.append("<access-policy>");
builder.append("<cross-domain-access>");
builder.append("<policy>");
builder.append("<allow-from http-request-headers=\"*\">");
builder.append("<domain uri=\"http://localhost:8183\"/>");
builder.append("</allow-from>");
builder.append("<grant-to>");
builder.append("<resource path=\"/customers/\" include-subpaths=\"true\"/>");
builder.append("</grant-to>");
builder.append("</policy>");
builder.append("</cross-domain-access>");
builder.append("</access-policy>");
return new StringRepresentation(builder.toString(), variant.getMediaType());
}
}
Constitution de l’interface Silverlight
L’application Silverlight que nous vous proposons est orientée informatique de gestion traditionnelle dans le sens où elle implémente le pattern Liste – Détail. Nous avons utilisé les composants proposés par défaut en Silverlight 2. Il serait possible d’enrichir cette application grâce aux composants évolués proposés dans le projet Silverlight Toolkit téléchargeable sur CodePlex (http://www.codeplex.com/silverlight).
Le projet joint à cet article comprend quatre composants Silverlight :
Composant | Description |
App.xaml | Point d’entrée de l’application Silverlight. Comprend notamment les ressources / styles. |
Home.xaml | Page principale, dans notre cas, il s’agit d’un onglet comprendant 2 section |
SearchCustomers.xaml | Chargement d’une liste de clients à partir du filtre positionné. Affichage du client sélectionné, et possibilité de la modifier ou de la supprimer. |
CreateCustomer.xaml | Ecran de création d’un nouveau client |
Figure 5 : Description des composants Silverlight
.jpg)
.jpg)
Figure 6 : Composants Recherche et Détail
Pour parfaire notre exemple, nous avons intégré la skin « Rough » proposée par Corrina Black sur son blog (http://blogs.msdn.com/corrinab/archive/2008/08/04/8828013.aspx), et mis à jour pour la version finale de Silverlight 2.
Remarque : Nous intégrons la skin « Rough » dans le code des exemples en téléchargements, dans la mesure où la skin disponible sur le blog de Corrina n’est pas alignée avec la version finale de Silverlight 2. Merci à Corrina qui a pris le temps d’avancer partiellement le portage et a accepté qoue nous vous la transmettions en preview. Néanmoins, le rendu visuel n’est pas opérationnel ni sous Visual Studio ni dans les Eclipse Tools for Silverlight parce que la Skin Rough n’arrive pas à être interprétée. Pour accéder au rendu visuel, il est indispensable de supprimer les ressources de Styles. Aussi, nous proposons plusieurs versions du code source de cet article (avec et sans ressources de Style).
Exécution des codes accompagnant cet article
Il est possible d’exécuter le code en environnement mixte Java / .Net ou bien pur Java. Pour le détail de ces configurations, nous vous invitons à consulter le billet « Architecture Series : Pure and Mixed configurations ». Le code accompagnant cet article vous est proposé sous 3 formes :
N° | Configuration | Description |
1 | Mixed Configuration - 4 XAML components - No Skin | Il s’agit de la configuration décrite plus haut. La skin Rough n’est pas intégrée afin que la prévisualiation Visual Studio soit opérationnelle. Le dossier DataGridRESTClient contient la solution VisualStudio Le dossier DataGridRESTService contient le projet Eclipse |
2 | Mixed Configuration - CustomerDetail component - Rough Skin | A des fins d’illustration, nous avons capitalisé sur les aspects mise à jour, création et suppression d’un client au sein d’un composant réutilisé « CustomerDetail.xaml ». La Skin Rough est intégrée par défaut dans cet exemple. Le dossier DataGridRESTClient contient la solution VisualStudio Le dossier DataGridRESTService contient le projet Eclipse |
3 | Pure Eclipse Configuration - all XAML inline - No Skin | Tous les projets peuvent être importés sous Eclipse (voir installation ci-après). Le code Silverlight a été regroupé dans le fichier Home.xaml. De plus la skin « Rough » n’est présente. La pré-visulisation ne fonctionne pas dans la version M2 d’Eclipse4SL. |
Exécution d’un environnement Mixte Visual Studio et Eclipse (archives 1 et 2)
Ouvrir la solution DataGridRESTClient dans Visual Studio auquel vous aurez préalablement ajouté les extensions pour Silverlight 2. Cliquer droit sur la page DataGridRESTClientTestPage.html du projet DataGridRESTClient.Web et choisir « View in Browser ».
Importer le projet DataGridRESTService dans votre environnement Eclipse auquel vous aurez préalablement ajouté le framework Restlet en version 1.1.2 (http://www.restlet.org/downloads/1.1/restlet-1.1.2.exe). Cliquer droit sur CustomerService.java et choisir « Run as … » puis « Java Application ».
Intégration dans l’environnement Eclipse Tools for Silverlight (archive 3)
Grâce à l’initiative Eclipse Tools for Silverlight (Eclipse4SL), il est possible d’exécuter l’ensemble des codes de l’article sous Eclipse. Néanmoins, dans la mesure où Eclipse4SL ne supporte pas complètement l’intégration de composants, nous vous proposons d’utiliser le code suivant, dans lequel nous avons remplacé le composant CustomerDetail par un code XAML inline.
Importer les trois projets dans votre environnement Eclipse auquel vous aurez préalablement ajouté le framework Restlet en version 1.1.2 (http://www.restlet.org/downloads/1.1/restlet-1.1.2.exe), et le plugin Eclipse4SL (www.eclipse4SL.org). Cliquer droit sur le projet DataGridRESTClient et choisir « Run as … » puis « Silverlight Application ».
A propos de l’auteur
Stève Sfartz est architecte en système d’informations chez Microsoft France. Il intervient principalement autour de scénarios d’innovations technologiques et d’interopérabilité sur les thèmes Cloud Computing, Web 2.0 et SOA. Retrouvez ses billets sur Cloud Computing @ Microsoft France, SOA @ Microsoft France, A Cup of Silverlight, et Think Big mais pas trop …
Cet article a été rédigé avec le concours de Jérôme Louvel et Thierry Boileau de la société Noelios Technologies.
Ressources
- [RV de l’interop] : RV de l’interopérabilité Silverlight
- [TechDays 2009] : Tour d’horizon REST
- [Site Web] : Eclipse Tools for Silverlight
- [Site Web] : Framework Restlet
- [Blog] : Silverlight, RIA et Interop