REST за разработчици на Java, Част 2: Restlet за уморените

API на Restlet с отворен код намалява натоварването, свързано с изграждането и консумирането на RESTful API в Java. В тази втора статия от поредицата REST за разработчици на Java Брайън Слетън ви запознава с Restlet и разглежда примерно приложение при разполагане на интерфейсите му в контейнерите за сървлети, които използвате днес, като същевременно се подготвя и за системите на бъдещето. Брайън също така представя накратко JSR 311: JAX-RS, усилията на Sun да интегрира RESTful API с Java EE стека.

Разработчиците на Java отдавна се интересуват от архитектурния стил REST, но малцина все още са изминали разстоянието между познатия свят на обектите и RESTful света на ресурсите. Въпреки че може да ни хареса фактът, че услугите RESTful могат да се произвеждат или консумират от други езици, мразим да се налага да конвертираме данни в и от байтови потоци. Мразим да се налага да мислим за HTTP, когато използваме инструменти като Apache HTTP Client. Ние гледаме с копнеж обектите, създадени от wsdl2javaкомандата, която ни позволява да предаваме аргументи в услуга SOAP толкова лесно, колкото всеки друг метод, изтривайки подробностите за извикване на отдалечена услуга под килима. И ние откриваме, че моделът на сървлета е малко прекалено изключен от произвежданите ресурси. Достатъчно е да кажем, че докато сме успели за да изградите RESTful услуги от нулата, това не е приятно изживяване.

REST за разработчици на Java

Прочетете поредицата:

  • Част 1: Става въпрос за информацията
  • Част 2: Почивка за уморени
  • Част 3: NetKernel

Политическите проблеми понякога усложняват техническите препятствия. Много мениджъри смятат, че базираните на SOAP уеб услуги са предписаният начин за изграждане на ориентирани към услуги архитектури (SOA) в Java EE. Това се променя с появата на важни дейности като JSR 311, JAX-RS: Java API за RESTful Web Services, за които ще научите в тази статия. Ако не друго, това усилие легитимира RESTful развитието в JEE пространството.

Междувременно пристигна помощ. По елегантен начин рамката с отворен код Restlet улеснява избягването на трънливите проблеми, които могат да възникнат при използването на традиционната технология JEE за изграждане и консумиране на RESTful услуги.

Корените на Рестлет

В опит да се справи с някои технически проблеми, свързани с извършването на REST с Java, Jérome Louvel, френски консултант по софтуер, се стреми да създаде рамка, която да осигури по-естествена форма. Първо той разгледа средата на NetKernel като отправна точка. Колкото и да му харесваше, той не беше идеален за API-фокусираната рамка, която той се опитваше да направи достъпна. Опитът обаче помогна да повлияе на мисленето му за видовете неща, които REST-ориентираната среда може да предложи. (Следващата статия от тази поредица ще изследва по-пълно NetKernel.)

Докато Лувел работеше върху неговата рамка, той разработи три цели:

  • Простите действия трябва да са лесни за основно използване. По подразбиране трябва да работят с минимални усилия, но също така да позволяват по-сложни конфигурации.
  • Кодът, написан в този API, трябва да бъде преносим през контейнери. Въпреки че системите, базирани на сървлети, могат да бъдат премествани между контейнери като Tomcat, Jetty и IBM WebSphere, Louvel имаше предвид по-голяма картина. Спецификацията на Servlet е свързана с HTTP и блокиращ I / O модел. Той искаше неговият API да бъде отделен и от двата, и да бъде разположен в контейнерите, използвани днес. Той също искаше те да бъдат използваеми с малко усилия в алтернативни и нововъзникващи контейнери като Grizzly, AsyncWeb и Simple Framework.
  • Той трябва да обогати не само сървърната страна за създаване на RESTful интерфейси в Java, но и клиентската страна. В HttpURLConnectionкласа и Apache HTTP Клиента са твърде ниско ниво, за да се интегрират чисто едно директно в повечето приложения.

Имайки предвид тези цели, той се зае да произведе Restlet API. След няколко години в поток, API стана стабилен и около него се разрасна общност. Днес основният API има жива потребителска база и тече значителна дейност за подпомагане на интеграцията с други инструменти и инициативи като JAX-RS. (Лувел вече е член на експертната група JAX-RS.)

Основи на Restlet

Основен сървър с Restlet API не би могъл да бъде по-лесен, както е показано в Листинг 1.

Листинг 1. Основен сървър с 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(); } }

Това приложение не прави много (с изключение на разпространението на настроението), но показва два от основните принципа на Restlet. Първо, простите неща са прости. Със сигурност са възможни по-сложни дейности, но вие се тревожите за тях само когато е необходимо. REST не му липсва способността да налага сигурност, ограничения, договаряне на съдържание или други важни задачи. Те остават до голяма степен ортогонални дейности, доста различни от процеса на удовлетворяване на RESTful API. Насложвате сложността според нуждите.

Второ, кодът в Листинг 1 е проектиран да бъде преносим сред типовете контейнери. Забележете, че той не посочва контейнер. Restlets са действителните ресурси, които в крайна сметка отговарят на исканията. Няма разлика между контейнера, обработващ заявката, и отговорника на информационния ресурс, както може да има в модела на сървлета. Ако въведете кода в IDE и добавите зависимости към org.restlet.jarи com.noelios.restlet.jarархивите, можете да стартирате приложението и трябва да видите съобщение в дневника като това:

Dec 7, 2008 11:37:32 PM com.noelios.restlet.http.StreamServerHelper start INFO: Starting the internal HTTP server

Насочете браузър към //localhost:8182и ще видите приятелския поздрав.

Зад кулисите org.restlet.jarсъдържа всички основни интерфейси за този API. В com.noelios.restlet.jarсъдържа основна изпълнение на тези интерфейси и осигурява възможност за работа с по подразбиране HTTP. Няма да искате да влезете в производство с този HTTP двигател, но той е изключително удобен за целите на разработката и тестването. Не е необходимо да стартирате голям контейнер, за да тествате кода си RESTful. В резултат на това тестването на модули и интеграция може да бъде много по-лесно.

Примерът в Листинг 1 използва много поведение по подразбиране, за да създаде Applicationекземпляр по подразбиране (ще обсъдя Applicationв следващия пример) и да слуша заявки за HTTP протокол на порт 8182. StreamServerHelperКласът започва да слуша на този порт и изпраща заявки към Restletекземпляра като те влизат.

Целта на Louvel да поддържа клиентска RESTful Java също е изпълнена с лекота, както можете да видите в Листинг 2.

Листинг 2. Клиент на 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); } }

Тъй като SimpleServerвсе още работи, стартирането на този нов клиентски код със същите зависимости на JAR трябва да отпечата приятелския поздрав на конзолата. Отпечатването на изхода в този стил очевидно не би работило за двоично ориентирани MIME типове, но отново е удобна отправна точка.

Пример без CRUD

Повечето педагогически примери REST показват CRUDish услуги (Create, Retrieve, Update, Delete) около прости обекти. Въпреки че този стил със сигурност работи добре с REST, той в никакъв случай не е единственият смисъл - и така или иначе повечето от нас са уморени от CRUD примери. Следващият пример демонстрира основите на приложението Restlet чрез опаковане на проверката на правописа на Jazzy с отворен код.

REST е за управление на информация, а не за извикване на произволно поведение, така че трябва да проявявате внимание, когато обмисляте API, ориентиран към поведение като Jazzy. Номерът е да се третира RESTful API като информационно пространство за думи, които съществуват и не съществуват в използваните речници. Проблемът може да бъде решен по различни начини, но тази статия ще дефинира две информационни пространства. /dictionaryсе използва за управление на думи в речника. /spellcheckerсе използва за намиране на предложения за думи, подобни на грешно написани думи. И двамата се фокусират върху информацията, като вземат предвид липсата или присъствието на думи в информационните пространства.

В RESTful архитектура, тази HTTP команда може да върне дефиниция на дума в речника:

GET //localhost:8182/dictionary/word

Вероятно ще върне HTTP кода за отговор „Не е намерен“ за думи, които не са в речника. В това информационно пространство е добре да се посочи, че думите не съществуват. Jazzy не предоставя определения за думи, така че ще оставя връщането на малко съдържание като упражнение за читателя.

Тази следваща HTTP команда трябва да добави дума към речника:

PUT // localhost: 8182 / речник / дума

Този пример използва, PUTзащото можете предварително да разберете какъв /dictionaryтрябва да бъде URI в информационното пространство и издаването на множество PUTs не трябва да прави разлика. ( PUTе идемпотентна заявка, като GET. Издаването на една и съща команда няколко пъти не би трябвало да има значение.) Ако искате да добавите дефиниции, можете да ги предадете като тела на PUTманипулатора. Ако искате да приемете множество дефиниции с течение на времето, може да пожелаете POSTтези дефиниции да бъдат включени, защото PUTе операция за презаписване.

Не пренебрегвайте синхронизирането

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се използва за изпращане в подчинените информационни пространства. В допълнение към извършването на това обвързване с контекст, той задава модел на URL, който позволява частта от думата на URL да бъде достъпна като атрибут в заявката. Това ще се използва в Restletсекциите, създадени в списъци 4 и 5.

Най- DictionaryRestlet, показан на Обява 4, е отговорна за разглеждане на заявленията за манипулиране на /dictionaryинформационното пространство.