%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% :JAX-RS %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \section{JAX-RS} JAX-RS je framework pro tvorbu RESTful webových služeb v~jazyce Java. Tak jako ostatní technologie je i~JAX-RS stavěný na servletech. Jak již název napovídá, tento framework byl navržen podle JAX-WS (viz. kapitola \ref{JAX-WS}). Programátorovi nabízí pohodlnou práci s~využitím anotací. \subsection{Servlet} RESTful webovou službu řídí servlet \texttt{ServletContainer} z~balíčku \texttt{com.sun.jersey.spi.container.servlet}. Je potřeba jej nastavit v~konfiguračním souboru \emph{web.xml}, jak bylo popsáno v~kapitole \ref{Deployement}. V~dalším textu bude tento servlet označován jako REST servlet. \subsection{URL vzory} URL vzory (URL patterns) jsou velmi pohodlný nástroj pro přidělování adres k~třídám reprezentujícím webové zdroje. Ve své podstatě se jedná o~šablonu, která je pak porovnána s~cílovou hodnotou. Pokud chceme v~URL vzoru uvést nějakou část adresy jako proměnnou, ohraničíme její název do složených závorek. Příklad ukazuje, jak vytvořit URL vzor s~proměnnou \texttt{animal}. \begin{Verbatim} /savci/{animal} \end{Verbatim} Pro reálnou adresu příchozího požadavku se pak hledá takový vzor, který ji odpovídá. Jestliže se najde, provede se promítnutí reálné adresy do vzoru a~tímto se zjistí hodnoty všech proměnných. Například adresa \emph{/savci/veverka\_obecna} by se porovnala se vzorem z~příkladu. Výsledkem porovnání by bylo, že adresa se shoduje se vzorem a~hodnota proměnné \texttt{animal} je řetězec \emph{veverka\_obecna}. \fcolorbox{boxout}{boxin}{ \parbox{0.98\linewidth}{ Přestože to v~dokumentaci frameworku není uvedené, používá JAX-RS velmi sofistikované srovnávání. Můžeme tedy deklarovat webový zdroj pro vyhledávání savců s~adresou \emph{/savci/vyhledavani}. Při příchodu požadavku servlet provede srovnání a~dostane dva možné výsledky. Zmíněný zdroj vyhledávání a~také potenciální zdroj zvířete se jménem \emph{vyhledavani} (pozn. v~této fázi není možné poznat, jestli například takovéto zvíře v~aplikaci existuje). Naštěstí má JAX-RS zabudovanou prioritu pro absolutně se shodující názvy zdrojů a~proto bude preferovat zdroj \emph{vyhledavani}. }} \subsection{Zdroje v~JAX-RS} V~JAX-RS je každý webový zdroj reprezentován samostatnou třídou. Zdroj reprezentující zvíře se bude jmenovat \texttt{Animal}. Ve frameworku JAX-RS se pro třídu reprezentující zdroj jako konvence používá přípona \texttt{\~{}Resource}. Takováto třída je doplněna anotací \texttt{@Path} z~balíčku \texttt{jawax.ws.rs}, která jako parametr bere URL vzor adresy daného zdroje. Příklad ukazuje kostru webového zdroje reprezentujícího zvíře v~aplikaci Animalia. \begin{Verbatim} @Path("/savci/{animal}") public class AnimalResource \{ \} \end{Verbatim} \subsection{Získání hodnoty proměnné z~adresy} Hodnotu proměnné z~reálné adresy zdroje lze získat snadno. Stačí deklarovat konstruktor, který bude brát parametr stejného typu, jako je proměnná v~adrese (JAX-RS zařídí případnou konverzi řetězce na číslo) a~tento parametr označit anotací \texttt{@PathParam} z~balíčku \texttt{javax.ws.rs}. Anotace \texttt{@PathParam} bere jako parametr název proměnné ve vzoru adresy. REST servlet pak sám zavolá konstruktor s~parametrem a~jeho hodnotu doplní. Příklad ukazuje, jak ve webovém zdroji \texttt{Animal} doplnit konstruktor s~proměnnou. \begin{Verbatim} @Path("/savci/{animal}") public class AnimalResource \{ \codeHighlight{private String animalId;} \codeHighlight{public AnimalResource(@PathParam("animal") String animalId) \{} \codeHighlight{ this.animalId = animalId;} \codeHighlight{\}} \} \end{Verbatim} \subsection{Metody webového zdroje} Metody webového zdroje deklarujeme stejně jako obyčejné metody. Poté každou z~nich doplníme anotací shodující se s~jménem HTTP metody. Pro metodu \texttt{GET} je to anotace \texttt{@GET}, obdobně pro ostatní metody. Pozor musíme dát na parametry. Metody označené anotacemi \texttt{@GET}, \texttt{@HEAD} a~\texttt{@DELETE} nesmí brát žádný parametr. To je logické, jelikož tyto metody v~HTTP požadavku neposílají žádná přiložená data v~jeho těle. Naopak metody doplněné anotací \texttt{@PUT} nebo \texttt{@POST} musí brát právě jeden parametr typu \texttt{String}. Příklad ukazuje, jak doplnit webový zdroj \texttt{AnimalResource} o~metodu \texttt{GET}, prozatím s~prázdným tělem. \begin{Verbatim} @Path("/savci/{animal}") public class AnimalResource \{ private String animalId; public AnimalResource(@PathParam("animal") String animalId) \{ this.animalId = animalId; \} \codeHighlight{@GET} \codeHighlight{public String get() \{} \codeHighlight{\}} \} \end{Verbatim} \subsection{Návratová hodnota metody} Metody webového zdroje mohou vracet hodnotu. U~metod doplněných anotací \texttt{@GET} je to dokonce povinnost. Framework JAX-RS umožňuje vracet řetězec, číslo nebo objekt typu \texttt{Response}. Asi nejpoužívanější je návratová hodnota v~podobě řetězce, který obsahuje XML, HTML nebo jiný kód. Takovýto řetězec je pak přímo vložen do těla odpovědi. Návratová hodnota v~podobě čísla je automaticky převedena na řetězec a~vložena do odpovědi stejným způsobem. V~obou těchto případech se používá odpověď \texttt{200 OK}. Pokud chceme použít jiný kód odpovědi, musíme použít objekt typu \texttt{Response}. Příklad ukazuje, jak s~využitím logické vrstvy (modelu) vrátit HTML reprezentaci požadovaného zvířete. \begin{Verbatim} @Path("/savci/{animal}") public class AnimalResource \{ private String animalId; public Animal(@PathParam("animal") String animalId) \{ this.animalId = animalId; \} @GET public String get() \{ \codeHighlight{ Animal animal = Animalia.getInstance().getAnimal(animalId);} \codeHighlight{ AnimalSerializer serializer = new AnimalHtmlSerializer();} \codeHighlight{ serializer.setAnimal(animal);} \codeHighlight{ return serializer.toString();} \} \} \end{Verbatim} \fcolorbox{boxout}{boxin}{ \parbox{0.98\linewidth}{ Oficiální dokumentace JAX-RS a~většina článků uvádí pouze výše zmíněné typy návratové hodnoty. Ve skutečnosti však framework nabízí ještě další možnost. Tou je vracení JSP stránky, která je pochopitelně před odesláním přeložena (viz. kapitola \ref{JSP}). To lze zařídit pomocí objektu typu \texttt{Viewable}, který je obsažen v~balíčku \texttt{com.sun.jersey.api.view}. Instanci třídy \texttt{Viewable} vyrobíme pomocí konstruktoru, kde prvním parametrem je adresářová cesta k~JSP souboru na serveru a~druhým je objekt zasílaný jako parametr. Příklad ukazuje, jak vrátit JSP stránku se zvířetem.\\ \\ \texttt{@GET}\\ \texttt{public Viewable get() \{}\\ \hspace*{3em}\texttt{Animal animal = Animalia.getInstance().getAnimal(animalId);}\\ \hspace*{3em}\texttt{return new Viewable("/WEB-INF/jsp/animal.jsp", animal);}\\ \texttt{\}}\\ \\ Objekt \texttt{animal} je tímto způsobem automaticky vložen do objektu požadavku a~na JSP stránce ho lze získat pomocí metody \texttt{getAttribute} (viz. kapitola \ref{JSP}). Název atributu je vždy \texttt{it}, získáme ho tedy zavoláním:\\ \\ \texttt{request.getAttribute("it");} }} \subsection{Přetížení metody GET} Metodu \texttt{GET} je možné v~JAX-RS přetížit podle typu návratové hodnoty. V~kapitole \ref{RESTful Web Services} jsme si řekli, že každý zdroj by měl nabízet dvě reprezentace --- jednu pro lidského uživatele a~druhou pro počítačové zpracování. K~přetížení metody \texttt{GET} podle návratového typu nelze v~JAX-RS použít pouze běžný postup z~jazyka Java, ale je potřeba využít anotace \texttt{@Produces} z~balíčku \texttt{javax.ws.rs}. Tato anotace bere jako parametr řetězec odrážející MIME typ návratové hodnoty. Příklad ukazuje, jak doplnit webový zdroj \texttt{Animal} o~metodu vracející XML reprezentaci zvířete. \begin{Verbatim} @Path("/savci/{animal}") public class AnimalResource \{ private String animalId; public Animal(@PathParam("animal") String animalId) \{ this.animalId = animalId; \} @GET \codeHighlight{@Produces("text/html")} public String \codeHighlight{getHtml()} \{ Animal animal = Animalia.getInstance().getAnimal(animalId); AnimalSerializer serializer = new AnimalHtmlSerializer(); serializer.setAnimal(animal); return serializer.toString(); \} \codeHighlight{@GET} \codeHighlight{@Produces("application/xml")} \codeHighlight{public String getXml() \{} \codeHighlight{ Animal animal = Animalia.getInstance().getAnimal(animalId);} \codeHighlight{ AnimalSerializer serializer = new AnimalXmlSerializer();} \codeHighlight{ serializer.setAnimal(animal);} \codeHighlight{ return serializer.toString();} \codeHighlight{\}} \} \end{Verbatim} \subsection{Objekt Response} Kromě řetězce můžeme také vracet hodnotu jako objekt typu \texttt{Response} z~balíčku \texttt{javax.ws.rs.core}. Práce s~třídou \texttt{Response} však bohužel nepatří k~tomu nejlepšímu a~nejpřehlednějšímu, co JAX-RS nabízí. Doporučuji ji využívat pouze v~případech, kdy není zbytí, například při vracení odpovědi \texttt{201 Created} a~podobných. Postup pro kladnou odpověď \texttt{200 OK} je následující. Na třídě \texttt{Response} zavoláme statickou metodu \texttt{ok}. Ta vrací odkaz na objekt typu \texttt{ResponseBuilder}, na kterém pak můžeme nastavovat pole hlavičky (metoda \texttt{header}), tělo odpovědi (metoda \texttt{entity}) a~další. Ze stavitelského objektu získáme instanci třídy \texttt{Response} zavoláním metody \texttt{build}. Třída \texttt{Response} nám kromě kladné odpovědi umožňuje vytvořit i~jiné odpovědi, třeba zmíněný \texttt{201 Created} (metoda \texttt{created}) nebo kterýkoliv kód (metoda \texttt{status}). Příklad ukazuje, jak obměnit \texttt{getHtml} metodu webového zdroje tak, aby vracela objekt typu \texttt{Response}. \begin{Verbatim} @Path("/savci/{animal}") public class AnimalResource \{ private String animalId; public Animal(@PathParam("animal") String animalId) \{ this.animalId = animalId; \} @GET @Produces("text/html") public \codeHighlight{Response} getHtml() \{ Animal animal = Animalia.getInstance().getAnimal(animalId); AnimalSerializer serializer = new AnimalHtmlSerializer(); serializer.setAnimal(animal); \codeHighlight{String body = serializer.toString();} \codeHighlight{return Response.ok().entity(body).build();} \} \} \end{Verbatim} \subsection{Výjimky} Prozatím jsme předpokládali, že zvíře se v~aplikaci vždy nachází. Uživatel ovšem může poslat požadavek na adresu \emph{/savci/liska\_podsita} a~takovéto zvíře v~aplikaci určitě není. Korektní odpověď na tento požadavek je tedy kód \texttt{404 Not Found}. V~JAX-RS lze tento kód vrátit pomocí vyvolání výjimky \texttt{WebApplicationException}, která jako parametr bere kód odpovědi. Příklad ukazuje, jak otestovat, jestli zvíře v~aplikaci je a~pokud ne, pak vyvolat výjimku \texttt{404}. Pro jednoduchost toto testování ukažme pouze na metodě vracející XML reprezentaci. \begin{Verbatim} @Path("/savci/{animal}") public class AnimalResource \{ private String animalId; public Animal(@PathParam("animal") String animalId) \{ this.animalId = animalId; \} @GET @Produces("application/xml") public String getXml() \{ Animal animal = Animalia.getInstance().getAnimal(animalId); \codeHighlight{if (animal == null) \{} \codeHighlight{ throw new WebApplicationException(404)}; \codeHighlight{\}} AnimalSerializer serializer = new AnimalXmlSerializer(); serializer.setAnimal(animal); return serializer.toString(); \} \} \end{Verbatim} \fcolorbox{boxout}{boxin}{ \parbox{0.98\linewidth}{ Specifikace HTTP nejen umožňuje, ale dokonce doporučuje k~některým odpovědím chybových hlášek přidávat tělo s~informacemi o~chybě. JAX-RS kupodivu nenabízí možnost vyvolat výjimku s~parametry \texttt{int, String}. Naproti tomu můžeme v~těle konstruktoru výjimky použít objekt typu \texttt{Response}. Ten sice umožňuje tělo odpovědi nastavit, ale má i~možnost nastavit kód odpovědi \texttt{200}, nebo jiný kladný. Vzniká kuriózní možnost vyvolat výjimku, která vlastně vůbec není výjimkou, ale kladnou odpovědí. Jedná se o~díru v~návrhu, která snad bude opravena v~některé další verzi JAX-RS. }} \subsection{Závěr} JAX-RS představuje pohodlný framework pro tvorbu RESTful webových služeb v~jazyce Java. Programátor se s~jeho použitím může plně zaměřit na návrh a~implementaci webových zdrojů. Přesto framework obsahuje některé nedopracované myšlenky. Třída \texttt{Response} sice umožňuje vytvořit odpovědi pro různé kódy pomocí pojmenovaných metod (např. zmíněná \texttt{created}), zdaleka však nepokrývá všechny dostupné. Nejsou zde přístupné pojmenované metody pro běžné odpovědi \texttt{400 Bad Request} ani \texttt{404 Not Found}. Namísto toho lze vyrobit odpověď \texttt{500 Internal Server Error} (metodou \texttt{serverError}), kterou programátor snad nikdy nebude chtít odesílat. Dále by se metody deklarované v~třídě \texttt{ResponseBuilder} měly podle konvencí jazyka Java namísto \texttt{entity} jmenovat \texttt{setEntity} atd. Také návrh webové výjimky (viz. rámeček) by snesl lepší propracování. I~přes tyto výhrady lze framework JAX-RS doporučit, jelikož popisované chyby nemají na práci s~frameworkem velký dopad. Použitá literatura:\\ \cite{JAX-RS Features} \emph{Přehled programování s~JAX-RS}\\ \cite{JAX-RS JavaDoc} \emph{Dokumentace tříd frameworku JAX-RS}\\