Google fait les cartes, Spring fait le REST

Logo google mapsLa cartographie est maintenant utilisée communément sur le Web. On ne peut plus consulter un site marchand sans pouvoir calculer son itinéraire jusqu’au point de vente, se renseigner sur un pays sans pouvoir explorer sa géographie, parfois même faire une sortie jogging ou vélo sans consulter ultérieurement son parcours sur une carte…

Les deux grands fournisseurs de cartographie sont actuellement Google avec sa Google Maps API et Microsoft avec Bing Maps. La plupart du temps, leur utilisation passe par du code Javascript exécuté côté client. Pourtant, il est parfois nécessaire de faire des traitements de géolocalisation côté serveur. Pour cela, les deux fournisseurs ont prévu une API de type REST. La suite de cet article ne s’intéressera plus qu’au monde “Google” et à son API, capable de générer une réponse de type XML ou JSON.

Be API avec le REST de Google !

Les principes de base

Pas de grosse surprise pour effectuer une requête géographique : on reste sur des principes REST classiques, avec une requête pour effectuer une géolocalisation inversée (à partir d’une adresse postale) du type :

http://maps.googleapis.com/maps/api/geocode/xml?address=90+rue+Baudin+92300+Levallois+Perret&sensor=false

Cette requête, qui peut être jouée directement sur un navigateur, permet de récupérer un message XML du type :

OK street_address <formatted_address>90 Rue Baudin, 92300 Levallois-Perret, France</formatted_address> <address_component> <long_name>90</long_name> <short_name>90</short_name> street_number </address_component> <address_component> <long_name>Rue Baudin</long_name> <short_name>Rue Baudin</short_name> route </address_component> <address_component> <long_name>Levallois-Perret</long_name> <short_name>Levallois-Perret</short_name> locality political </address_component> <address_component> <long_name>Hauts-de-Seine</long_name> <short_name>92</short_name> administrative_area_level_2 political </address_component> <address_component> <long_name>Île-de-France</long_name> <short_name>IdF</short_name> administrative_area_level_1 political </address_component> <address_component> <long_name>France</long_name> <short_name>FR</short_name> country political </address_component> <address_component> <long_name>92300</long_name> <short_name>92300</short_name> postal_code </address_component> 48.8989222 2.2877605 <location_type>RANGE_INTERPOLATED</location_type> 48.8975800 2.2864066 48.9002780 2.2891046 48.8989222 2.2877507 48.8989358 2.2877605

Le message résultat est immédiatement compréhensive à un esprit humain. C’est sans doute pour cela qu’il n’est absolument pas documenté par Google, qui ne fournit pas même un XSD…  🙁

REST ou JSON ?

Google préconise plutôt l’utilisation du service JSON.

JSON est clairement une notation beaucoup plus compacte que le verbeux XML et donc plus efficace. Cependant, elle n’offre pas tout l’attirail de XML, notamment en ce qui concerne la description des signatures de service et le contrôle des formats des messages. J’ai toujours tendance à privilégier XML pour les échanges de serveur à serveur et JSON pour les interactions internes à un système (interrogation AJAX d’un client web sur son serveur par exemple).

On est ici sur des échanges serveur à serveur, donc va pour le XML !

Comment conjuguer REST et Spring

La classe RestTemplate

Spring fournit la classe RestTemplate, qui elle-même fournit des méthodes de haut niveau pour invoquer les 6 méthodes principales du protocole HTTP. Elle simplifie énormément la programmation de la partie cliente d’un appel REST en gérant toute la plomberie habituelle liée à l’utilisation du protocole HTTP et la conversion des objets Java.

La lecture de la doc Spring conduit vite à la constatation que l’appel du service Google maps précédent peut se faire sous la forme :

restTemplate.getForObject( "http://maps.googleapis.com/maps/api/geocode/xml?address={address}&sensor=false", String.class,"90 rue Baudin, 92300 Levallois Perret");

L’encapsulation de JAXB 2

Le code suivant est simple et de bon aloi, mais ne résout pas la problématique du mapping du résultat (ici un flux XML) vers le graphe d’objets Java.

Heureusement, et c’est là qu’entre la magie du RESTTemplate, il peut être configuré à l’aide d’un marshaller fourni par Spring :

org.springframework.oxm.jaxb.Jaxb2Marshaller

Rien de plus à ajouter que de référencer cette classe, à la fois comme marshaller et comme unmarshaller pour retrouver son modèle métier directement mappé sur le flux XML ! C’est de la pure magie, mais c’est bon !

Un petit projet vaut mieux qu’un long post de blog…

La Mise en place Maven

Il n’existe pas à ma connaissance d’archetype tout prêt pour faire du Spring. Pour notre exemple, nous partirons donc d’un archetype simple, en tapant la commande suivante :

mvn archetype:generate \ -DarchetypeArtifactId=maven-archetype-quickstart \ -DgroupId=fr.ippon.gmap \ -DartifactId=restgmap \ -Dversion=1.0-SNAPSHOT

On obtient ainsi un projet restgmap, dans lequel nous allons nous empresser de modifer le fichier pom.xml pour notamment ajouter les dépendances à Spring :

junit junit 4.10 test org.apache.log4j com.springsource.org.apache.log4j 1.2.15 org.apache.commons com.springsource.org.apache.commons.logging 1.1.1 org.springframework org.springframework.core ${spring.version} org.springframework org.springframework.test ${spring.version} test org.springframework org.springframework.context ${spring.version} org.springframework spring-oxm ${spring.version} org.springframework org.springframework.web ${spring.version} <spring.version>3.0.5.RELEASE</spring.version> com.springsource.repository.bundles.release SpringSource Enterprise Bundle Repository - SpringSource Bundle Releases http://repository.springsource.com/maven/bundles/release com.springsource.repository.bundles.external SpringSource Enterprise Bundle Repository - External Bundle Releases http://repository.springsource.com/maven/bundles/external

La création du modèle

L’analyse de la réponse fournie lors d’une interrogation du service de géolocalisation permet assez facilement de construire un modèle objet comme suit :

Modèle de géolocalisation

Ce modèle en poche, les annotations JAXB se font très facilement même s’il aurait été plus simple de faire tout cela à partir d’une XSD, directement fournie par Google…

On a par exemple la classe Geometry comme suit :

package fr.ippon.gmap.model.gmap; import javax.xml.bind.annotation.XmlElement; public class Geometry { @XmlElement private GeoPosition location; @XmlElement private Viewport viewport; @XmlElement private Viewport bounds; public GeoPosition getLocation() { return location; } }

Pour avoir toutes les informations sur les classes annotées, vous pouvez vous référerez au code qui est disponible sur GitHub à l’adresse : https://github.com/bpinel/restgmap

Le service et son test unitaire

Le reste du code est tout ce qu’il y a de plus standard. Et je vous laisse découvrir le service et les tests unitaires dans le projet git.