REST za programere Java, 2. dio: Restlet za umorne

API otvorenog koda Restlet smanjuje opterećenje uključeno u izgradnju i potrošnju RESTful API-ja u Javi. U ovom drugom članku u seriji REST za programere za Java , Brian Sletten vas upoznaje s Restletom i prolazi kroz primjer aplikacije za postavljanje njegovih sučelja u spremnike servleta koje danas koristite, a istovremeno se priprema za sustave budućnosti. Brian također ukratko predstavlja JSR 311: JAX-RS, Sun-ov napor da integrira RESTful API-je sa Java EE stogom.

Programeri Java dugo su bili zainteresirani za arhitekturni stil REST, ali malo je njih još prešlo udaljenost između poznatog svijeta predmeta i RESTful svijeta resursa. Iako nam se može svidjeti činjenica da RESTful usluge mogu proizvesti ili konzumirati drugi jezici, mrsko nam je pretvaranje podataka u i iz bajt tokova. Mrzimo što moramo razmišljati o HTTP-u kada koristimo alate poput Apache HTTP klijenta. Čezljivo gledamo objekte stvorene wsdl2javanaredbom, koja nam omogućuje prosljeđivanje argumenata u SOAP uslugu jednako lako kao i poziv bilo koje druge metode, brišući detalje pozivanja udaljene usluge pod tepih. I nalazimo da je model servleta samo malo previše odvojen od resursa koji se proizvode. Dovoljno je reći da dok smo bili u mogućnosti graditi RESTful usluge od nule, nije bilo ugodno iskustvo.

OSTALO za programere Java

Pročitajte seriju:

  • 1. dio: Radi se o informacijama
  • Dio 2: Restlet za umorne
  • Dio 3: NetKernel

Politička pitanja ponekad su stvarala tehničke prepreke. Mnogi upravitelji smatraju da su web usluge temeljene na SOAP-u propisani način izgradnje arhitektura orijentiranih na usluge (SOA) u Javi EE. To se mijenja pojavom važnih aktivnosti kao što su JSR 311, JAX-RS: Java API za RESTful Web Services, o čemu ćete naučiti u ovom članku. Ako ništa drugo, ovaj napor legitimira RESTful razvoj u JEE prostoru.

U međuvremenu je stigla pomoć. Na elegantan način, okvir otvorenog koda Restlet olakšava izbjegavanje trnovitih problema koji mogu nastati korištenjem tradicionalne JEE tehnologije za izgradnju i upotrebu RESTful usluga.

Restletovi korijeni

U nastojanju da riješi neka tehnička pitanja koja se tiču ​​REST-a s Javom, Jérome Louvel, francuski savjetnik za softver, nastojao je stvoriti okvir koji bi omogućio prirodnije prilagođavanje. Kao polazište prvo je pogledao NetKernel okruženje. Koliko god mu se to sviđalo, nije bilo savršeno uklapanje u okvir usmjeren na API koji je nastojao učiniti dostupnim. Iskustvo je ipak pomoglo utjecati na njegovo razmišljanje o vrstama stvari koje okruženje orijentirano na odmor može ponuditi. (Sljedeći će članak u ovoj seriji detaljnije istražiti NetKernel.)

Kako je Louvel radio na svom okviru, razvio je tri cilja:

  • Jednostavne radnje trebale bi biti jednostavne za osnovnu uporabu. Zadane postavke trebale bi raditi s minimalnim naporom, ali također trebaju omogućiti složenije konfiguracije.
  • Kôd napisan na ovaj API trebao bi biti prenosiv u sve spremnike. Iako se sustavi temeljeni na servletima mogu premještati među spremnike kao što su Tomcat, Jetty i IBM WebSphere, Louvel je imao na umu širu sliku. Specifikacija servleta vezana je uz HTTP i blokirni U / I model. Želio je da se njegov API može odvojiti od njih i rasporediti u spremnike koji se danas koriste. Također je želio da budu upotrebljivi s malo truda u zamjenskim i nadolazećim spremnicima poput Grizzlyja, AsyncWeba i Simple Framework-a.
  • Trebao bi obogatiti ne samo poslužiteljsku proizvodnju RESTful sučelja u Javi, već i klijentsku. HttpURLConnectionKlasa i Apache HTTP klijent preniske razine integrirati čisto izravno u većini aplikacija.

Imajući ove ciljeve na umu, krenuo je u proizvodnju API-ja Restlet. Nakon nekoliko godina promjena, API je postao stabilan i oko njega je rasla zajednica. Danas osnovni API ima živu bazu korisnika i u tijeku su značajne aktivnosti za podršku integraciji s drugim alatima i inicijativama poput JAX-RS. (Louvel je sada u stručnoj skupini JAX-RS.)

Osnove restleta

Osnovni poslužitelj s Restlet API-jem ne može biti lakši, kao što je prikazano u Popisu 1.

Popis 1. Osnovni poslužitelj s Restletom

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(); } }

Ova aplikacija ne donosi puno (osim što širi dobro raspoloženje), ali pokazuje dva temeljna Restletova principa. Prvo, jednostavne stvari su jednostavne. Složenije aktivnosti svakako su moguće, ali brinete se o njima samo kad trebate. REST-u ne nedostaje sposobnost nametanja sigurnosti, ograničenja, pregovaranja o sadržaju ili drugih važnih zadataka. To su uglavnom ortogonalne aktivnosti, sasvim različite od procesa zadovoljavanja RESTful API-ja. Po potrebi složite složenost.

Drugo, kôd s popisa 1 dizajniran je za prijenos među vrstama spremnika. Primijetite da ne navodi spremnik. Restletto su stvarni resursi koji u konačnici odgovaraju na zahtjeve. Ne postoji razlika između spremnika koji obrađuje zahtjev i odgovora resursa informacija, kao što to može biti u modelu servleta. Ako utipkate kôd u IDE i dodate ovisnosti o org.restlet.jari com.noelios.restlet.jararhivama, možete pokrenuti aplikaciju i trebao bi vidjeti poruku dnevnika poput ove:

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

Usmjerite preglednik prema //localhost:8182i trebali biste vidjeti prijateljski pozdrav.

Iza kulisa org.restlet.jarsadrži sva glavna sučelja za ovaj API. com.noelios.restlet.jarSadrži osnovne provedbu tih sučelja i pruža rukovanje sposobnost zadani HTTP. Nećete htjeti ići u proizvodnju s ovim HTTP motorom, ali je izuzetno prikladan za razvoj i testiranje. Ne morate pokretati glavni spremnik da biste testirali svoj RESTful kôd. Rezultat je jedinstveno i integracijsko testiranje.

Uzorak iz popisa 1 koristi puno zadanog ponašanja za stvaranje zadane Applicationinstance (raspravit ću Applicationu sljedećem primjeru) i preslušavanje zahtjeva HTTP protokola na portu 8182. StreamServerHelperKlasa započinje preslušavanje na ovom portu i upućuje zahtjeve Restletinstance kao ulaze.

Louvelov cilj podržavanja RESTful Java na strani klijenta također je ispunjen s lakoćom, kao što možete vidjeti na popisu 2.

Popis 2. Restlet klijent

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); } }

Dok je i SimpleServerdalje pokrenut, pokretanje ovog novog klijentskog koda s istim JAR ovisnostima trebalo bi ispisati prijateljski pozdrav na konzolu. Ispis rezultata u ovom stilu očito ne bi radio za binarno orijentirane MIME vrste, ali opet je prikladno polazište.

Primjer koji nije CRUD

Većina pedagoških primjera REST pokazuju CRUDish usluge (Create, Retrieve, Update, Delete) oko jednostavnih predmeta. Iako taj stil zasigurno dobro funkcionira s REST-om, to nikako nije jedini pristup koji ima smisla - a većini nas je ionako dosta CRUD primjera. Sljedeći primjer pokazuje osnove aplikacije Restlet zamatanjem Jazzy provjere pravopisa otvorenog koda.

REST se odnosi na upravljanje informacijama, a ne pozivanje na proizvoljno ponašanje, pa trebate biti oprezni kada razmišljate o API-ju usmjerenom na ponašanje poput Jazzyja. Trik je tretirati RESTful API kao informacijski prostor za riječi koje postoje i ne postoje u rječnicima koji se koriste. Problem bi se mogao riješiti na razne načine, ali ovaj će članak definirati dva informacijska prostora. /dictionarykoristi se za upravljanje riječima u rječniku. /spellcheckerkoristi se za pronalaženje prijedloga za riječi slične pogrešno napisanim riječima. Oboje se usredotočuju na informacije uzimajući u obzir odsutnost ili prisutnost riječi u informacijskim prostorima.

U RESTful arhitekturi, ova HTTP naredba može vratiti definiciju riječi u rječniku:

GET //localhost:8182/dictionary/word

Vjerojatno bi vratio kôd HTTP odgovora "Nije pronađeno" za riječi koje nisu u rječniku. U ovom je informacijskom prostoru u redu naznačiti da riječi ne postoje. Jazzy ne daje definicije za riječi, pa ću vraćanje dijela sadržaja ostaviti kao vježbu čitatelju.

Sljedeća HTTP naredba trebala bi dodati riječ u rječnik:

PUT // localhost: 8182 / rječnik / riječ

Ovaj se primjer koristi PUTjer možete prethodno shvatiti koliki /dictionarybi trebao biti URI u informacijskom prostoru, a izdavanje višestrukih PUTs ne bi trebalo biti bitno. ( PUTje idempotentni zahtjev, poput GET. Izdavanje iste naredbe više puta ne bi trebalo značiti.) Ako želite dodati definicije, možete ih proslijediti kao tijela PUTobrađivaču. Ako želite s vremenom prihvatiti više definicija, možda ćete poželjeti POSTte definicije, jer PUTje to operacija prepisivanja.

Ne previdite sinkronizaciju

Kako bi primjeri bili fokusirani, ovaj članak ne obraća posebnu pozornost na probleme sinkronizacije. Ne ponašajte se prema svom proizvodnom kodu tako nonšalantno! Više informacija potražite u resursu kao što je Java Concurrency in Practice .

U Restletslučajevima da ću stvoriti potrebu za ograničavanjem na odgovarajućim informacijskim prostorima, kao što je prikazano u Listing 3.

Popis 3. Jednostavna provjera pravopisa RESTful

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 Routerkoristi se za slanje u podređene informacijske prostore. Uz izvođenje ovog kontekstualnog vezivanja, postavlja obrazac URL-a koji omogućuje da dio URL-a s riječju bude dostupan kao atribut na zahtjevu. To će se iskoristiti u Restlets kreiranim u popisima 4 i 5.

The DictionaryRestlet, prikazan u Popisu 4, odgovoran je za obradu zahtjeva za manipulacijom /dictionaryinformacijskim prostorom.