Web Service y w Javie Konrad Miziński Wydział Elektroniki i Technik Informacyjnych, Politechnika Warszawska ul. Nowowiejska 15/19, 00-665 Warszawa, Polska k.mizinski@stud.elka.pw.edu.pl Streszczenie. Niniejszy artykuł opisuje Web Services jako jeden ze sposobów integracji systemów. Na wstępie przybliża technologie pozwalające na ich realizację w języku Java oraz podstawy protokołu HTTP. Następnie opisuje 2 powszechnie wykorzystywane typy Web Service ów - oparte o standardy SOAP oraz REST. 1. Definicja. Organizacja W3C definiuje Web Service w sposób następujący: A Web service is a software system designed to support interoperable machine-to-machine interaction over a network () [1]. W definicji tej należy zwrócić uwagę na 2 aspekty. Web Service y to takie systemy, które zapewniają komunikację pomiędzy maszynami (a więc bez udziału człowieka) oraz przez sieć. Najczęściej komunikacja ta odbywa się za pomocą protokołu HTTP, ale spotykane są również realizacje oparte o HTTPS (bezpieczną wersję protokołu HTTP) czy SMTP. 2. Protokół HTTP. HTTP (ang. Hypertext Transfer Protocol) to protokół sieci WWW służący do przesyłania dokumentów hipertekstowych (np. stron HTML czy danych w formacie XML lub JSON). Jego podstawową cechą jest działanie w trybie żądanie - odpowiedź, co ilustruje rysunek 1. Rysunek 1. Schemat działania protokołu http. Wydruk 1. ilustruje przykład żądania HTTP. W pierwszej linii zawiera on nazwę metody protokołu HTTP (najczęściej GET lub POST, ale spotykane są też inne), identyfikator zasobu, do którego się odwołujemy oraz wersję protokołu. w kolejnych liniach widzimy różnego typy nagłówki informujące m. in. o typie przesyłanej zawartości czy akceptowalnych typach dokumentów przyjmowanych w odpowiedzi. Na uwagę zasługuje nagłówek Host - wymagany wersji 1.1 protokołu HTTP. Następnie (po znaku powrotu karetki i nowej linii) przesyłana jest zawartość naszego żądania. W tym przypadku jest do dokument w formacie XML. Wydruk 2. zawiera przykład odpowiedzi protokołu HTTP. Najważniejszym jej elementem jest kod odpowiedzi informujący o statusie wykonania żądania. W tym przypadku mamy do czynienia z najczęściej spotykanym kodem 200 OK informującym o poprawnym wykonaniu żądania. Content-Type: application/xml; charset=utf-8 Accept: image/gif, */* User-Agent: Mozilla/5.0
<?xml version="1.0"?> <test /> Wydruk 1. Przykład żądania HTTP HTTP/1.1 200 OK Content-Type: text/xml; charset=utf-8 Content-Length: nnn <?xml version="1.0"?> <test /> Wydruk 2. Przykład odpowiedzi HTTP 3. JAXB. Zanim przejdziemy do omawiania Web Service ów koniecznie jest zapoznanie się z technologią JAXB. W dużym skrócie służy on do serializacji obiektów języka na dokumenty w formacie XML. Mapowanie klas na poszczególne elementy plików XML zazwyczaj wykonuje się za pomocą adnotacji. I tak za pomocą @RootElement oznaczamy klasę będącą głównym elementem naszego dokumentu, a jej pola, o ile są to pojedyncze obiekty, oznaczamy przez @XmlElement. W przypadku kolekcji możemy zastosować adnotację @XmlElementWrapper. Klasy będące podrzędnymi elementami dokumentu XML adnotujemy za pomocą @XmlType. Możemy też definiować typy wyliczeniowe (adnotacja @XmlEnum), oznaczać pola jako niesierializowalne do XML (@XmlTransient) czy korzystać z przestrzeni nazw (@XmlSchema). Wydruk 3. zawiera przykłady klas opatrzonych adnotacjami JAXB. Na uwagę zasługuje zastosowania adnotacji @XmlTypew przypadku klasy Course (w przeciwieństwie do klasy Student oznaczonej jako @XmlElement) oraz wykorzystanie @XmlElementWrapper w przypadku listy przedmiotów. Przykład obiektu zserializowanego do formatu XML przedstawiony jest na wydruku 4. @XmlRootElement(name="student") @XmlAccessorType(XmlAccessType.FIELD) public class Student { private Long id; private String name; private String surname; @XmlElementWrapper @XmlElement(name = "course") private List<Course> courses; @XmlType @XmlAccessorType(XmlAccessType.FIELD) public class Course { private String name; private Double degree; Wydruk 3. Definicje klas zawierające adnotacje JAXB. <?xml version="1.0" encoding="utf-8"?> <student> <id>2</id> <name>jan</name> <surname>kowalski</surname> <courses> <course> <name>analiza</name> <degree>2.0</degree> </course> <course> <name>algebra</name> <degree>3.5</degree> </course> </courses> </student> Wydruk 4. Obiekt języka Java zserializowany do postaci XML. 4. SOAP Web Services. Pierwszy opisywany rodzaj Web Service ów definiuje standard JAX- WS (ang. Java API for XML Web Services). Składają się na niego 2 podstawowe technologie format SOAP służący do przesyłania
wiadomości oraz język WSDL opisujący ich strukturę. 4.1 SOAP. Protokół SOAP (ang. Simple Object Access Protocol) jest prostym protokołem komunikacyjnym pozwalającym na przesyłanie wiadomości w formacie opartym na standardzie XML. Struktura komunikatu SOAP przedstawiona została na rysunku 2. ="http://www.w3.org/2001/12/soap-envelope" soap:encodingstyle ="http://www.w3.org/2001/12/soap-encoding"> <soap:header /> <soap:body xmlns:m="http://mizinski.waw.pl/hello"> <m:helloworld> <name>adam</name> </ m:helloworld> </soap:body> </soap:envelope> Wydruk 5. Żądanie SOAP. HTTP/1.1 200 OK Content-Type: text/xml; charset=utf-8 Content-Length: nnn Rysunek 2. Struktura komunikatu SOAP. Nadrzędnym znacznikiem dokumentu SOAP jest znacznik <envelope> zawierający w sobie opcjonalny znacznik <header> (zazwyczaj pusty element, bądź w ogóle pomijany) oraz znacznik <body> zawierający właściwą treść naszego komunikatu. Wydruki 5. i 6. przedstawiają przykłady i odpowiedzi z zastosowaniem protokołu SOAP. Wydruk 7. zawiera przykład odpowiedzi na błędnie wykonane żądanie. Zawiera on kolejny opcjonalny element protokołu SOAP tzn. znacznik <foalt> zawierający szczegóły błędu, w typowym przypadku jest to zrzut stosu wywołań w momencie rzucenia wyjątku. Na uwagę zasługuje wykorzystanie jedynie metody POST protokołu HTTP oraz prostych kodów błędu 200 i 500. POST /service HTTP/1.1 Content-Type: application/soap+xml; charset=utf-8 <?xml version="1.0"?> <soap:envelope xmlns:soap <?xml version="1.0"?> <soap:envelope xmlns:soap= "http://www.w3.org/2001/12/soap-envelope" soap:encodingstyle= "http://www.w3.org/2001/12/soap-encoding"> <soap:body> <m:helloworldresponse xmlns:m="http://mizinski.waw.pl/hello"> <greeting>hello Adam!</greeting> </ m:helloworldresponse /> </soap:body> </soap:envelope> Wydruk 6. Odpowiedź SOAP. HTTP/1.1 500 Server Error Content-Type: texml; charset=utf-8 Content-Length: nnn <soap:envelope xmlns:soap="http://schemas.xmlsoap.or g/soap/envelope/"> <soap:body> <soap:fault> <faultcode>soap:client</faultcode> <faultstring>...</faultstring> </soap:fault> </soap:body> </soap:envelope> Wydruk 7. Fault informacja o błędzie. 4.2 WSLD. Do opisu komunikatów SOAP służy język WSDL (ang. Web Services Description Language). Bazuje on na języku XML oraz
opisującym go standardzie XML Schema. Pozwala jednoznacznie określić jakie komunikaty mogą być przesyłane za pomocą danego Web Service u. Definiuje także strukturę przysyłanego żądania oraz otrzymywanej odpowiedzi. Ostatnią istotną informacją zapisaną w dokumencie WSDL jest lokalizacja opisywanego Web Service u, która wcale nie musi być zgodna z lokalizacją, z której ten dokument został pobrany. Dokument WSLD stanowi tym samym swoisty kontrakt pomiędzy dostawcą Web Service u a jego klientem. Dodatkową zaletą tego języka jest możliwość generacji dokumentów WSDL bezpośrednio z klas języka Java (można to zrobić np. za pomocą przeglądarki internetowej dopisując?wsdl do adresu URL naszego Web Service u) oraz odtworzenia tych klas z dokumentu WSDL. 4.3 Tworzenie Web Service u. Do utworzenia Web Service u wykorzystujemy adnotacje języka Java. Interfejs zawierający udostępniane przez nas metody oznaczamy jako @WebService, jego metody jako @WebMethod (adnotacja ta bywa pomijana przez programistów gdyż jest domyślna dla metod interfejsu oznaczonego przez @WebService, ale zazwyczaj występuje w kodzie automatycznie wygenerowanym z pliku WSDL), zaś argumenty i wartości zawracane poszczególnych funkcji odpowiednio jako @WebParam i @WebResult. Wydruk 8.przedstawia przykład opatrzonego odpowiednimi adnotacjami interfejsu oraz klasy go implementującej. W celu uruchomienia naszej usługi najlepiej jest skorzystać z jednej z gotowych implementacji standardu Web Services (np. popularnego Apache CXF), albo użyć klasy Endpoint ze standardowej biblioteki języka Java. @WebService public interface StudentService { @WebResult(name = "student") Student getstudent( @WebParam(name = "id")long id); @WebService(endpointInterface = "pl.waw.mizinski.sdm.soap.studentservice") public class StudentsServiceImpl implements StudentService { @Override public Student getstudent(long id) { return null; Wydruk 8. Implementacja Web Service u w technologii SOAP. 5. RESTFul Web Services. REST (ang. Representational State Transfer) jest wzorcem architektonicznym opartym na protokole HTTP. Pozwala w większym stopniu wykorzystać cechy tego protokołu niż JAX-WS. Podstawowym jego założeniem jest wykorzystanie wszystkich metod HTTP, a więc już nie tylko GET i PUT, również PUT, DELETE itd. Pozwala to na wykonanie na interesującym nas zasobie podstawowych operacji CRUD, i tak GET oznacza pobranie danych, POST ich modyfikację, PUT i DELETE odpowiednio ich dodanie oraz usunięcie. Drugim fundamentem architektury REST jest wykorzystanie URI jako jednoznacznego identyfikatora zasobu, np. http://<adres endpointa>/student/1 może oznaczać studenta o identyfikatorze 1. Do istotnych cech RESTA należy też jego bezstanowość (ang. Stateless). Oznacza to, że serwer nie posiada informacji o aktualnym stanie klienta, a wysyłane do niego żądania muszą zawierać komplet informacji potrzebnych do ich obsłużenia. Przykłady wywołań Web Service ów RESTowych przedstawiono na wydruku 9. Uwagę zwraca fakt, że operacje pobrania i usunięcia studenta nie zawierają treści żądania HTPP jedynie nazwę metody, ścieżkę do zasobu i niezbędne nagłówki. Również dodanie studenta jest operacją lżejszą niż w przypadku JAX-WS ponieważ nie zawiera elementów <envelope> czy <body> protokołu SOAP.
Wydruk 10. zawiera odpowiedź na żądanie pobrania danych studenta. PUT /service/student HTTP/1.1 Content-Type: application/xml; charset=utf-8 <student><id>1</id><name>jan</name> <surname>kowalski</surname><courses> <course><name>analiza</name><degree> 2.0</degree></course>></courses></student> DELETE /service/student/1 HTTP/1.1 GET /service/student/1 HTTP/1.1 Wydruk 9. Żądania w architekturze REST. HTTP/1.1 200 OK Date: Thu, 07 Jan 2016 18:40:11 GMT Content-Type: application/xml Content-Length: 260 <?xml version="1.0" encoding="utf-8" standalone="yes"?><student><id>1</id> <name>jan</name><surname>kowalski</surn ame> <courses><course><name>analiza</name><de gree>2.0 </degree></course><course><name>algebra</ name> <degree>3.5</degree></course></courses></st udent> Wydruk 10. Odpowiedź w architekturze REST. 5.1 Kody odpowiedzi HTTP. Kolejną cechą architektury SOAP jest możliwość skorzystania z całej palety kodów odpowiedzi HTTP. I tak w przypadku powodzenia zleconej operacji możemy skorzystać z kodów z rodziny 2xx. Przykładem może być odpowiedź 201 Created wysłana po poprawnym wykonaniu żądania PUT. Szczególnie użyteczna może być odpowiedź 204 No Content pozwalająca nam poinformować, że klient nie powinien spodziewać się żadnej treści w odpowiedzi na swoje żądanie. Jeszcze większe możliwości dają nam kody informujące o błędach. Pozwalają nie tylko stwierdzić czy błąd leży po stronie klienta (rodzina kodów 4xx) czy serwera (rodzina 5xx), ale również precyzyjnie wskazać przyczynę błędu. Przykładem może być chociażby popularny kod 404 Not Found informujący o braku zasobu, którego dotyczy nasze żądanie, czy bardziej precyzyjny 410 Gone czyli informacja, że żądany zasób został usunięty. W przypadku gdy sam kod odpowiedzi okazuje się niewystarczający mamy możliwość dodania do odpowiedzi dowolnych nagłówków, w szczególności zawierających szczegóły napotkanego błędu. Oczywiście nie unikniemy popularnego kodu 500 Internal Server Error, ale powinniśmy używać go używać tylko na najbardziej ogólnym poziomie obsługi błędów w nieprzewidzianych przez programistę przypadkach. 5.2 Implementacja. Podobnie jak w przypadku JAX-WS RESTowe Web Service y implementujemy za pomocą adnotacji języka Java. Wskazujemy m. in. metodę HTTP którą chcemy zaimplementować. Służą do tego adnotacje @HttpMethod, @GET, @POST, @DELETE, @PUT, @OPTIONS czy @HEAD (Szczególnie ciekawa jest nie wspomniana wcześniej metoda HEAD pobierająca tylko nagłówki odpowiedzi. Wywołana zamiast metody GET pozwala w łatwy sposób sprawdzić, czy dany zasób istnieje bez konieczności jego pobierania). Za pomocą adnotacji @Path wskazujemy pod jakim adresem dany zasób jest dostępny zaś adnotacje @Consumes i @Produces pozwalające określić w jakich formatach zostaną przesłane treści żądania i odpowiedzi (Ciekawostką jest że możemy udostępnić dane w kilku formatach, pozwalając tym samym klientowi, z którego z nich chce korzystać). Bardzo bogaty jest również zbiór adnotacji pozwalający na wydobywanie parametrów z żądania. Możemy np. pobrać parametry podane po znaku zapytania na końcu adresu URL (adnotacja @QueryParam) lub bezpośrednio w jego ścieżce (@PathParam, @MatrixParam).
Możemy również skorzystać ze znanego z formularzy sposobu ich przekazywania (rozdzielone dwukropkiem pary klucz wartość w treści żądania, adnotacja @FormParam) czy przesłać je w nagłówku lub ciasteczku (odpowiednio @HeaderParam, @CokieParam). Dodatkowo mamy możliwość określenia domyślnych wartości parametrów (adnotacja @DefaultValue) czy opakowywania większej ich ilości w pojedyncze klasy (@DefaultValue). Przykład opatrzonej odpowiednimi adnotacjami klasy przedstawiony został na wydruku 11. Na uwagę zasługuje sygnatura metody getstudent zawierająca zarówno adnotacje @GET jak i @HEAD oraz rzucany przez nią wyjątek WebApplicationException pozwalający na ustawienie odpowiedniego statusu odpowiedzi i przekazanie szczegółów błędu w nagłówku. Całość przykładów w kompilowanej wersji dostępna jest pod adresem https://github.com/mizin1/sdm2/[3]. return; Wydruk 11. Implementacja Web Service u w architekturze REST. Literatura [1] Web Services Glossary, http://www.w3.org/tr/wsgloss/#webservice, dostęp 8 stycznia 2016. [2] REST ciekawszy sposób na komunikację client-server, http://www.yarpo.pl/2012/07/29/restciekawszy-sposob-na-komunikacjeclient-server/, dostęp 8 stycznia 2016. [3] Pełne kody źródłowe przykładów, https://github.com/mizin1/sdm2, dostęp 24 stycznia 2016. @Path("/student" public class StudentRestService { @GET @HEAD @Path("/{id") @Produces("text/xml") public Student getstudent( @PathParam("id") Long id) { throw new WebApplicationException(Response.status( Response.Status.NOT_FOUND).header("reason", "deleted").build()); @PUT public Response addstudent(student student) { return Response.status( Response.Status.CREATED).build(); @DELETE @Path("/{id") public void deletestudent(@pathparam("id")long id) {