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 wsdl2javaBefehl 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 HttpURLConnectionKlasse 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. Restlets 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.jarund com.noelios.restlet.jarArchiven, 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.jarenthält das alle wichtigen Schnittstellen für diese API. Das com.noelios.restlet.jarenthä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 Applicationim nächsten Beispiel darauf eingehen) und auf HTTP-Protokollanforderungen an Port 8182 zu warten. Die StreamServerHelperKlasse beginnt, diesen Port abzuhören und sendet Anforderungen an die RestletInstanz 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 SimpleServerneue 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. /dictionarywird verwendet, um Wörter im Wörterbuch zu verwalten. /spellcheckerwird 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, PUTweil Sie /dictionaryim Voraus herausfinden können, wie der URI im Informationsbereich aussehen soll, und die Ausgabe mehrerer PUTs keinen Unterschied machen sollte. ( PUTDies 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 PUTHandler übergeben. Wenn Sie im Laufe der Zeit mehrere Definitionen akzeptieren möchten, möchten Sie POSTdiese Definitionen möglicherweise in, da dies PUTeine Ü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 Routerwird 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 Restletin den Listen 4 und 5 erstellten s genutzt.

Das DictionaryRestletin Listing 4 gezeigte ist für die Bearbeitung von Anforderungen zur Manipulation des /dictionaryInformationsraums verantwortlich.