REST für Java-Entwickler, Teil 2: Restlet für Müde
Die Open-Source-Restlet-API reduziert die Arbeitslast beim Erstellen und Konsumieren von RESTful-APIs in Java. In diesem zweiten Artikel in der REST for Java-Entwicklerreihe führt Brian Sletten Sie in Restlet ein und führt Sie durch eine Beispielanwendung, in der die Schnittstellen in den heute verwendeten Servlet-Containern bereitgestellt werden, und bereitet sich gleichzeitig auf die Systeme der Zukunft vor. Brian stellt außerdem kurz JSR 311: JAX-RS vor, Suns Bemühungen, RESTful-APIs in den Java EE-Stack zu integrieren.
Java-Entwickler haben sich seit langem für den REST-Architekturstil interessiert, aber nur wenige haben bisher die Distanz zwischen der vertrauten Welt der Objekte und der RESTful-Welt der Ressourcen zurückgelegt. Obwohl uns die Tatsache gefällt, dass RESTful-Services von anderen Sprachen erstellt oder verwendet werden können, hassen wir es, Daten in und aus Byte-Streams konvertieren zu müssen. Wir hassen es, über HTTP nachdenken zu müssen, wenn wir Tools wie den Apache HTTP Client verwenden. Wir betrachten sehnsüchtig Objekte, die mit dem wsdl2java
Befehl erstellt wurden. Dadurch können wir Argumente so einfach wie bei jedem anderen Methodenaufruf an einen SOAP-Dienst übergeben und die Details des Aufrufs eines Remotedienstes unter den Teppich kehren. Und wir finden, dass das Servlet-Modell etwas zu weit von den produzierten Ressourcen entfernt ist. Es genügt zu sagen, dass wir in der Lage waren Es war keine angenehme Erfahrung, RESTful-Services von Grund auf neu zu erstellen.
REST für Java-Entwickler
Lesen Sie die Serie:
- Teil 1: Es geht um die Information
- Teil 2: Restlet für die Müden
- Teil 3: NetKernel
Politische Probleme haben manchmal die technischen Hürden verschärft. Viele Manager sind der Ansicht, dass SOAP-basierte Webdienste die vorgeschriebene Methode zum Erstellen serviceorientierter Architekturen (SOAs) in Java EE sind. Dies ändert sich mit dem Aufkommen wichtiger Aktivitäten wie JSR 311, JAX-RS: Die Java-API für RESTful Web Services, die Sie in diesem Artikel kennenlernen werden. Nicht zuletzt legitimiert diese Anstrengung die RESTful-Entwicklung im JEE-Bereich.
Inzwischen ist Hilfe angekommen. Auf elegante Weise macht es das Open Source Restlet-Framework einfach, die heiklen Probleme zu vermeiden, die durch die Verwendung traditioneller JEE-Technologie zum Erstellen und Konsumieren von RESTful-Diensten entstehen können.
Restlets Wurzeln
Um einige der technischen Probleme bei der Durchführung von REST mit Java zu lösen, versuchte Jérome Louvel, ein französischer Softwareberater, ein Framework zu schaffen, das eine natürlichere Anpassung bietet. Er betrachtete zunächst die NetKernel-Umgebung als Ausgangspunkt. So sehr es ihm auch gefiel, es passte nicht perfekt zu dem API-fokussierten Framework, das er zur Verfügung stellen wollte. Die Erfahrung hat jedoch dazu beigetragen, sein Denken über die Art von Dingen zu beeinflussen, die eine REST-orientierte Umgebung bieten kann. (Im nächsten Artikel dieser Reihe wird NetKernel ausführlicher behandelt.)
Während Louvel an seinem Framework arbeitete, entwickelte er drei Ziele:
- Einfache Aktionen sollten für die grundlegende Verwendung einfach sein. Die Standardeinstellungen sollten mit minimalem Aufwand funktionieren, aber auch komplexere Konfigurationen ermöglichen.
- In diese API geschriebener Code sollte über Container hinweg portierbar sein. Obwohl servletbasierte Systeme zwischen Containern wie Tomcat, Jetty und IBM WebSphere verschoben werden können, hatte Louvel ein größeres Bild im Auge. Die Servlet-Spezifikation ist an HTTP und ein blockierendes E / A-Modell gebunden. Er wollte, dass seine API von beiden trennbar und in den heute verwendeten Containern einsetzbar ist. Er wollte auch, dass sie mit geringem Aufwand in alternativen und aufkommenden Containern wie Grizzly, AsyncWeb und dem Simple Framework verwendet werden können.
- Es sollte nicht nur die Serverseite bei der Erstellung von RESTful-Schnittstellen in Java bereichern, sondern auch die Clientseite. Die
HttpURLConnection
Klasse und der Apache HTTP-Client sind zu niedrig, um sauber und direkt in die meisten Anwendungen integriert zu werden.
Mit diesen Zielen vor Augen machte er sich daran, die Restlet-API zu erstellen. Nach einigen Jahren im Wandel wurde die API stabil und eine Community wuchs um sie herum. Heutzutage verfügt die Kern-API über eine lebendige Benutzerbasis, und es werden umfangreiche Aktivitäten zur Unterstützung der Integration mit anderen Toolkits und Initiativen wie JAX-RS durchgeführt. (Louvel ist jetzt Mitglied der JAX-RS-Expertengruppe.)
Grundlagen zum Wiederherstellen
Ein Basisserver mit der Restlet-API könnte möglicherweise nicht einfacher sein, wie in Listing 1 gezeigt.
Listing 1. Ein Basisserver mit 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(); } }
Diese Anwendung macht nicht viel (außer gute Laune verbreiten), zeigt aber zwei der Grundprinzipien von Restlet. Erstens sind einfache Dinge einfach. Komplexere Aktivitäten sind sicherlich möglich, aber Sie sorgen sich nur dann darum, wenn Sie es brauchen. REST fehlt es nicht an der Fähigkeit, Sicherheit, Einschränkungen, Inhaltsverhandlungen oder andere wichtige Aufgaben durchzusetzen. Diese bleiben weitgehend orthogonale Aktivitäten, die sich deutlich von dem Prozess der Erfüllung einer RESTful-API unterscheiden. Sie überlagern die Komplexität nach Bedarf.
Zweitens ist der Code in Listing 1 so konzipiert, dass er zwischen Containertypen portierbar ist. Beachten Sie, dass kein Container angegeben wird. Restlet
s sind die tatsächlichen Ressourcen, die letztendlich auf die Anforderungen reagieren. Es gibt keinen Unterschied zwischen dem Container, der die Anforderung verarbeitet, und dem Informationsressourcen-Responder, wie dies im Servlet-Modell der Fall sein kann. Wenn Sie den Code in eine IDE geben und fügen Sie Abhängigkeiten von den org.restlet.jar
und com.noelios.restlet.jar
Archiven, können Sie die Anwendung ausführen und eine Log - Meldung wie diese sehen sollte:
Dec 7, 2008 11:37:32 PM com.noelios.restlet.http.StreamServerHelper start INFO: Starting the internal HTTP server
Zeigen Sie mit einem Browser auf //localhost:8182
, und Sie sollten die freundliche Begrüßung sehen.
Hinter den Kulissen org.restlet.jar
enthält das alle wichtigen Schnittstellen für diese API. Das com.noelios.restlet.jar
enthält eine grundlegende Implementierung dieser Schnittstellen und bietet eine Standardfunktion für die HTTP-Verarbeitung. Sie möchten mit dieser HTTP-Engine nicht in Produktion gehen, sie ist jedoch für Entwicklungs- und Testzwecke äußerst praktisch. Sie müssen keinen Hauptcontainer starten, um Ihren RESTful-Code zu testen. Unit- und Integrationstests können daher viel einfacher sein.
Das Beispiel in Listing 1 verwendet viel Standardverhalten, um eine Standardinstanz zu erstellen Application
(ich werde Application
im nächsten Beispiel darauf eingehen) und auf HTTP-Protokollanforderungen an Port 8182 zu warten. Die StreamServerHelper
Klasse beginnt, diesen Port abzuhören und sendet Anforderungen an die Restlet
Instanz als sie kommen herein.
Louvels Ziel, clientseitiges RESTful Java zu unterstützen, wird ebenfalls problemlos erreicht, wie Sie in Listing 2 sehen können.
Listing 2. Ein Restlet-Client
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); } }
Wenn der SimpleServer
neue Clientcode mit denselben JAR-Abhängigkeiten noch ausgeführt wird, sollte die freundliche Begrüßung auf der Konsole ausgedruckt werden. Das Drucken der Ausgabe in diesem Stil würde natürlich nicht für binär orientierte MIME-Typen funktionieren, ist aber wiederum ein praktischer Ausgangspunkt.
Nicht-CRUD-Beispiel
Die meisten pädagogischen REST-Beispiele zeigen CRUDish-Dienste (Erstellen, Abrufen, Aktualisieren, Löschen) für einfache Objekte. Obwohl dieser Stil mit REST sicherlich gut funktioniert, ist er keineswegs der einzige sinnvolle Ansatz - und die meisten von uns haben sowieso keine CRUD-Beispiele mehr. Das folgende Beispiel zeigt die Grundlagen einer Restlet-Anwendung, indem Sie die Jazzy Open Source-Rechtschreibprüfung einschließen.
Bei REST geht es darum, Informationen zu verwalten und kein willkürliches Verhalten aufzurufen. Daher müssen Sie vorsichtig sein, wenn Sie eine verhaltensorientierte API wie Jazzy in Betracht ziehen. Der Trick besteht darin, die RESTful-API als Informationsbereich für Wörter zu behandeln, die in den verwendeten Wörterbüchern vorhanden sind und nicht. Das Problem kann auf verschiedene Arten gelöst werden. In diesem Artikel werden jedoch zwei Informationsräume definiert. /dictionary
wird verwendet, um Wörter im Wörterbuch zu verwalten. /spellchecker
wird verwendet, um Vorschläge für Wörter zu finden, die falsch geschriebenen Wörtern ähnlich sind. Beide konzentrieren sich auf Informationen, indem sie das Fehlen oder Vorhandensein von Wörtern in den Informationsräumen berücksichtigen.
In einer RESTful-Architektur kann dieser HTTP-Befehl eine Definition eines Wortes im Wörterbuch zurückgeben:
GET //localhost:8182/dictionary/word
Es würde wahrscheinlich den HTTP-Antwortcode "Not Found" für Wörter zurückgeben, die nicht im Wörterbuch enthalten sind. In diesem Informationsbereich kann angegeben werden, dass keine Wörter vorhanden sind. Jazzy bietet keine Definitionen für Wörter, daher überlasse ich es dem Leser, einige Inhalte als Übung zurückzugeben.
Dieser nächste HTTP-Befehl sollte dem Wörterbuch ein Wort hinzufügen:
PUT // localhost: 8182 / dictionary / word
In diesem Beispiel wird verwendet, PUT
weil Sie /dictionary
im Voraus herausfinden können, wie der URI im Informationsbereich aussehen soll, und die Ausgabe mehrerer PUT
s keinen Unterschied machen sollte. ( PUT
Dies ist beispielsweise eine idempotente Anforderung GET
. Wenn Sie denselben Befehl mehrmals ausführen, sollte dies keinen Unterschied machen.) Wenn Sie Definitionen hinzufügen möchten, können Sie diese als Textkörper an den PUT
Handler übergeben. Wenn Sie im Laufe der Zeit mehrere Definitionen akzeptieren möchten, möchten Sie POST
diese Definitionen möglicherweise in, da dies PUT
eine Überschreibungsoperation ist.
Übersehen Sie nicht die 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
wird verwendet, um an die untergeordneten Informationsräume zu versenden. Zusätzlich zur Durchführung dieser Kontextbindung wird ein URL-Muster eingerichtet, mit dem der "Wort" -Teil der URL als Attribut für die Anforderung verfügbar ist. Dies wird in den Restlet
in den Listen 4 und 5 erstellten s genutzt.
Das DictionaryRestlet
in Listing 4 gezeigte ist für die Bearbeitung von Anforderungen zur Manipulation des /dictionary
Informationsraums verantwortlich.