REST pour les développeurs Java, partie 2: Restlet pour les fatigués
L'API Restlet open source réduit la charge de travail impliquée dans la création et la consommation d'API RESTful en Java. Dans ce deuxième article de la série REST pour développeurs Java , Brian Sletten vous présente Restlet et parcourt un exemple d'application en déployant ses interfaces dans les conteneurs de servlet que vous utilisez aujourd'hui, tout en vous préparant aux systèmes du futur. Brian présente également brièvement JSR 311: JAX-RS, l'effort de Sun pour intégrer les API RESTful à la pile Java EE.
Les développeurs Java s'intéressent depuis longtemps au style architectural REST, mais peu ont encore parcouru la distance entre le monde familier des objets et le monde des ressources RESTful. Bien que nous aimions le fait que les services RESTful puissent être produits ou consommés par d'autres langages, nous détestons avoir à convertir des données vers et à partir de flux d'octets. Nous détestons avoir à penser à HTTP lors de l'utilisation d'outils comme Apache HTTP Client. Nous regardons avec envie les objets créés par la wsdl2java
commande, ce qui nous permet de passer des arguments dans un service SOAP aussi facilement que n'importe quel autre appel de méthode, balayant les détails de l'appel d'un service distant sous le tapis. Et nous trouvons que le modèle de servlet est juste légèrement trop déconnecté des ressources produites. Qu'il suffise de dire que pendant que nous avons pu pour créer des services RESTful à partir de zéro, cela n'a pas été une expérience agréable.
REST pour les développeurs Java
Lisez la série:
- Partie 1: Il s'agit de l'information
- Partie 2: Restlet pour les fatigués
- Partie 3: NetKernel
Les problèmes politiques ont parfois aggravé les obstacles techniques. De nombreux gestionnaires estiment que les services Web basés sur SOAP sont le moyen prescrit de créer des architectures orientées services (SOA) dans Java EE. Cela change avec l'émergence d'activités importantes telles que JSR 311, JAX-RS: l'API Java pour les services Web RESTful, que vous découvrirez dans cet article. Si rien d'autre, cet effort légitime le développement RESTful dans l'espace JEE.
Pendant ce temps, l'aide est arrivée. De manière élégante, le framework Restlet open source permet d'éviter facilement les problèmes épineux qui peuvent résulter de l'utilisation de la technologie JEE traditionnelle pour créer et consommer des services RESTful.
Les racines de Restlet
Dans un effort pour résoudre certains des problèmes techniques liés à la réalisation de REST avec Java, Jérome Louvel, un consultant logiciel français, a cherché à créer un cadre qui fournirait un ajustement plus naturel. Il a d'abord examiné l'environnement NetKernel comme point de départ. Autant qu'il l'aimait, cela ne correspondait pas parfaitement au cadre axé sur l'API qu'il cherchait à rendre disponible. L'expérience l'a aidé à influencer sa réflexion sur le genre de choses qu'un environnement orienté REST peut offrir, cependant. (Le prochain article de cette série explorera plus complètement NetKernel.)
Pendant que Louvel travaillait sur son cadre, il a développé trois objectifs:
- Les actions simples doivent être simples pour une utilisation de base. Les valeurs par défaut doivent fonctionner avec un minimum d'effort, mais permettent également des configurations plus complexes.
- Le code écrit dans cette API doit être portable entre les conteneurs. Bien que les systèmes basés sur des servlets puissent être déplacés entre des conteneurs tels que Tomcat, Jetty et IBM WebSphere, Louvel avait une vision plus large en tête. La spécification du servlet est liée à HTTP et à un modèle d'E / S bloquant. Il voulait que son API soit séparable des deux et déployable dans les conteneurs utilisés aujourd'hui. Il souhaitait également qu'ils soient utilisables avec peu d'effort dans des conteneurs alternatifs et émergents tels que Grizzly, AsyncWeb et Simple Framework.
- Cela devrait enrichir non seulement le côté serveur de la production d'interfaces RESTful en Java, mais également le côté client. La
HttpURLConnection
classe et le client HTTP Apache sont de niveau trop bas pour s'intégrer proprement et directement dans la plupart des applications.
Avec ces objectifs à l'esprit, il a entrepris de produire l'API Restlet. Après quelques années de flux, l'API est devenue stable et une communauté s'est développée autour d'elle. Aujourd'hui, l'API principale dispose d'une base d'utilisateurs dynamique et une activité importante est en cours pour soutenir l'intégration avec d'autres boîtes à outils et initiatives telles que JAX-RS. (Louvel fait maintenant partie du groupe d'experts JAX-RS.)
Principes de base de Restlet
Un serveur de base avec l'API Restlet ne pourrait pas être plus facile, comme le montre le listing 1.
Liste 1. Un serveur de base avec Restlet
package net.bosatsu.restlet.basic; import org.restlet.Restlet; import org.restlet.Server; import org.restlet.data.MediaType; import org.restlet.data.Protocol; import org.restlet.data.Request; import org.restlet.data.Response; public class SimpleServer { public static void main(String[]args) throws Exception { Restlet restlet = new Restlet() { @Override public void handle(Request request, Response response) { response.setEntity("Hello, Java RESTafarians!", MediaType.TEXT_PLAIN); } }; // Avoid conflicts with other Java containers listening on 8080! new Server(Protocol.HTTP, 8182, restlet).start(); } }
Cette application ne fait pas grand-chose (sauf répandre la bonne humeur), mais elle montre deux des principes fondamentaux de Restlet. Premièrement, les choses simples sont simples. Des activités plus complexes sont certainement possibles, mais vous ne vous en souciez que lorsque vous en avez besoin. REST ne manque pas de capacité à appliquer la sécurité, les contraintes, la négociation de contenu ou d'autres tâches importantes. Celles-ci restent des activités largement orthogonales, bien distinctes du processus de satisfaction d'une API RESTful. Vous superposez la complexité au besoin.
Deuxièmement, le code du listing 1 est conçu pour être portable parmi les types de conteneurs. Notez qu'il ne spécifie pas de conteneur. Restlet
s sont les ressources réelles qui répondent finalement aux demandes. Il n'y a pas de distinction entre le conteneur traitant la demande et le répondeur de la ressource informationnelle, comme cela peut être le cas dans le modèle de servlet. Si vous tapez le code dans un IDE et ajoutez des dépendances sur les archives org.restlet.jar
et com.noelios.restlet.jar
, vous pouvez exécuter l'application et vous devriez voir un message de journal comme celui-ci:
Dec 7, 2008 11:37:32 PM com.noelios.restlet.http.StreamServerHelper start INFO: Starting the internal HTTP server
Pointez un navigateur vers //localhost:8182
, et vous devriez voir le message d'accueil amical.
Dans les coulisses, le org.restlet.jar
contient toutes les principales interfaces de cette API. Le com.noelios.restlet.jar
contient une implémentation de base de ces interfaces et fournit une capacité de gestion HTTP par défaut. Vous ne voudrez pas entrer en production avec ce moteur HTTP, mais il est exceptionnellement pratique à des fins de développement et de test. Vous n'avez pas besoin de démarrer un conteneur majeur pour tester votre code RESTful. Les tests unitaires et d'intégration peuvent donc être beaucoup plus faciles.
L'exemple de la liste 1 utilise beaucoup de comportements par défaut pour créer une Application
instance par défaut (je parlerai Application
dans l'exemple suivant) et écouter les requêtes de protocole HTTP sur le port 8182. La StreamServerHelper
classe commence à écouter sur ce port et envoie les requêtes à l' Restlet
instance comme ils entrent.
L'objectif de Louvel de prendre en charge Java RESTful côté client est également atteint avec facilité, comme vous pouvez le voir dans le Listing 2.
Listing 2. Un client Restlet
package net.bosatsu.restlet.basic; import java.io.IOException; import org.restlet.Client; import org.restlet.data.Protocol; public class SimpleClient { public static void main(String [] args) throws IOException { String uri = (args.length > 0) ? args[0] : "//localhost:8182" ; Client client = new Client(Protocol.HTTP); client.get(uri).getEntity().write(System.out); } }
Avec le SimpleServer
toujours en cours d'exécution, le lancement de ce nouveau code client avec les mêmes dépendances JAR devrait imprimer le message d'accueil convivial sur la console. Imprimer la sortie dans ce style ne fonctionnerait évidemment pas pour les types MIME orientés binaire mais, encore une fois, c'est un point de départ pratique.
Exemple non CRUD
La plupart des exemples pédagogiques REST montrent des services CRUDish (Créer, Récupérer, Mettre à jour, Supprimer) autour d'objets simples. Bien que ce style fonctionne certainement bien avec REST, ce n'est en aucun cas la seule approche qui ait du sens - et la plupart d'entre nous en ont assez des exemples CRUD, de toute façon. L'exemple suivant illustre les bases d'une application Restlet en encapsulant le vérificateur d'orthographe open source Jazzy.
REST consiste à gérer les informations, pas à invoquer un comportement arbitraire, vous devez donc faire preuve de prudence lorsque vous envisagez une API orientée comportement comme Jazzy. L'astuce consiste à traiter l'API RESTful comme un espace d'informations pour les mots qui existent et n'existent pas dans les dictionnaires utilisés. Le problème peut être résolu de différentes manières, mais cet article définira deux espaces d'informations. /dictionary
est utilisé pour gérer les mots du dictionnaire. /spellchecker
est utilisé pour trouver des suggestions de mots similaires à des mots mal orthographiés. Les deux se concentrent sur l'information en considérant l'absence ou la présence de mots dans les espaces d'information.
Dans une architecture RESTful, cette commande HTTP pourrait renvoyer une définition d'un mot dans le dictionnaire:
GET //localhost:8182/dictionary/word
Il renverrait probablement le code de réponse HTTP "Not Found" pour les mots qui ne sont pas dans le dictionnaire. Dans cet espace d'informations, il est bon d'indiquer que les mots n'existent pas. Jazzy ne fournit pas de définitions pour les mots, je vais donc laisser le retour du contenu comme exercice pour le lecteur.
Cette prochaine commande HTTP devrait ajouter un mot au dictionnaire:
PUT // localhost: 8182 / dictionnaire / mot
Cet exemple utilise PUT
parce que vous pouvez déterminer à l' /dictionary
avance ce que l'URI dans l' espace d'informations doit être, et l'émission de plusieurs PUT
s ne devrait pas faire de différence. ( PUT
est une requête idempotente, comme GET
. Lancer la même commande plusieurs fois ne devrait pas faire de différence.) Si vous souhaitez ajouter des définitions, vous pouvez les transmettre en tant que corps au PUT
gestionnaire. Si vous souhaitez accepter plusieurs définitions au fil du temps, vous pouvez souhaiter POST
ces définitions dans, car il PUT
s'agit d'une opération d'écrasement.
Ne négligez pas la synchronisation
In the interest of keeping the examples focused, this article pays no special attention to synchronization issues. Do not treat your production code so nonchalantly! Consult a resource such as Java Concurrency in Practice for more information.
The Restlet
instances that I'll create need to be bound to the appropriate information spaces, as shown in Listing 3.
Listing 3. A simple RESTful spell checker
package net.bosatsu.restlet.spell; import com.swabunga.spell.event.SpellChecker; import com.swabunga.spell.engine.GenericSpellDictionary; import com.swabunga.spell.engine.SpellDictionary; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import org.restlet.data.Protocol; import org.restlet.*; public class SpellCheckingServer extends Application { public static String dictionary = "Restlet/dict/english.0"; public static SpellDictionary spellingDict; public static SpellChecker spellChecker; public static Restlet spellCheckerRestlet; public static Restlet dictionaryRestlet; static { try { spellingDict = new GenericSpellDictionary(new File(dictionary)); spellChecker = new SpellChecker(spellingDict); spellCheckerRestlet = new SpellCheckerRestlet(spellChecker); dictionaryRestlet = new DictionaryRestlet(spellChecker); } catch (Exception e) { e.printStackTrace(); } } public static void main(String [] args) throws Exception { Component component = new Component(); component.getServers().add(Protocol.HTTP, 8182); SpellCheckingServer spellingService = new SpellCheckingServer(); component.getDefaultHost().attach("", spellingService); component.start(); } public Restlet createRoot() { Router router = new Router(getContext()); router.attach("/spellchecker/{word}", spellCheckerRestlet); router.attach("/dictionary/{word}", dictionaryRestlet); return router; } }
After it builds up the dictionary instance and the spell checker, the Restlet setup in Listing 3 is slightly more complicated than in the earlier basic example (but not much!). The SpellCheckingServer
is an instance of a Restlet Application
. An Application
is an organizational class that coordinates deployment of functionally connected Restlet
instances. The surrounding Component
asks an Application
for its root Restlet
by calling the createRoot()
method. The root Restlet
returned indicates who should respond to the external requests. In this example, a class called Router
est utilisé pour envoyer aux espaces d'informations subordonnés. En plus d'effectuer cette liaison de contexte, il met en place un modèle d'URL qui permet à la partie «mot» de l'URL d'être disponible en tant qu'attribut sur la demande. Cela sera mis à profit dans les Restlet
s créés dans les listes 4 et 5.
Le DictionaryRestlet
, illustré dans le Listing 4, est responsable du traitement des demandes de manipulation de l' /dictionary
espace d'informations.