serviciu web, JAX-WS, JAX-RS, XML, SOAP, WSDL, UDDI, HTTP, URI, MIME, AJAX, DWR, endpoint, SEI, JAXB, xjc, schemagen, XSD, GlassFish open-source server
Un serviciu web reprezintă o funcţionalitate implementată în cadrul unei aplicaţii, accesibilă printr-o reţea de calculatoare (cel mai frecvent prin Internet, folosind protocolul HTTP), oferind interoperabilitate între programe ce rulează pe platforme diferite. O caracteristică a serviciilor web este extensibilitatea şi descrierea funcţionalităţii într-un limbaj ce poate fi procesat automat, astfel încât să poată fi prelucrat cu uşurinţă. Asocierea (slab cuplată) a mai multor servicii web se urmăreşte pentru rezolvarea unor probleme complexe. Astfel, programe ca oferă funcţionalităţi de bază pot interacţiona pentru a oferi servicii sofisticate, cu valoare adăugată. Comunicaţia între entităţile implicate în procesul de implementare şi interogare a serviciilor web este realizată prin mesaje, ceea ce nu presupune cunoaşterea capabilităţilor de care dispun platformele pe care rulează acestea. De regulă, arhitectura după care sunt modelate serviciile web este cea de client-server.
Implementarea serviciilor web se poate face în mai multe moduri, distingându-se serviciile web de dimensiuni “mari” şi serviciile web fără stare (eng. RESTful web services).
Serviciile web de dimensiuni “mari”, implementate în Java prin tehnologia JAX-WS, folosesc pentru comunicaţie mesaje XML care respectă standardul SOAP (Simple Object Access Protocol) – spre a descrie arhitectura şi formatul mesajelor. Operaţiile pe care le pun la dispoziţie serviciile pot fi procesate în mod automat, întrucât interfeţele (care le conţin) sunt descrise în limbajul WSDL (Web Services Description Language). Acesta este un limbaj bazat pe XML prin intermediul căruia sunt definite din punct de vedere sintactic interfețele ce includ funcționalitățile implementate de serviciul web. Atât standardul SOAP folosit pentru formatul mesajelor cât şi limbajul de definire a interfeţelor WSDL au fost adoptate pe scară largă.
Un serviciu web modelat după standardul SOAP trebuie să conţină următoarele elemente:
Serviciile web fără stare sunt adecvate unor scenarii de integrare ad-hoc, folosind standarde W3C sau IETF (ca HTTP, XML, URI, MIME) – deci o infrastructură mai accesibilă – ceea scade costul cu dezvoltarea, astfel că adoptarea lor impune mai puţine restricţii. Un test pentru a determina dacă un serviciu web este independent de stare constă în verificarea comportamentului acestuia în situația repornirii serverului care îl găzduiește. Sunt mai bine integrate cu HTTP decât serviciile bazate pe SOAP (nu mai sunt necesare mesaje în format XML sau descrieri ale serviciilor bazate pe WSDL). Serviciile web de tip REST-ful (eng. Representational State Transfer) includ informațiile cu privire funcționalitatea pe care o oferă în mesajele schimbate între client și server.
Situaţiile în care este utilă construirea unor servicii web fără stare este:
Serviciile web de dimensiuni mari sunt folosite în probleme specifice aplicaţiilor integrate pentru întreprinderi, având cerinţe mai complexe în privinţa calităţii serviciilor, în timp ce serviciile web fără stare sunt folosite pentru probleme de interfaţare prin reţea.
Tehnologia JAX-WS implementează cerinţe mai complexe referitoare la calitatea serviciilor (QoS – Quality of Service), frecvent întâlnite în aplicaţiile integrate pentru întreprinderi, oferind suport pentru mai multe protocoale care oferă standarde pentru securitate şi fiabilitate, asigurând interoperabilitatea între servere şi clienţi conforme cu aceste protocoale pentru servicii web.
Tehnologia JAX-RS facilitează procesul de dezvoltare a unor aplicaţii web conforme arhitecturii REST pentru a integra anumite proprietăţi în acestea precum: cuplarea slabă (dezvoltarea serverului se poate face fără afectarea clienţilor existenţi) sau scalabilitate, bazându-se pe o arhitectură simplă. Clienţii pot folosi o parte sau toate serviciile web puse la dispoziţie în timp ce serverul poate fi dezvoltat, integrându-le şi cu alte servicii web.
JAX-WS (Java API for XML Web Services) este o tehnologie Java folosită pentru dezvoltarea de servicii web şi de aplicaţii care le invocă, definit de JSR224, comunicaţia bazându-se pe XML. Utilizatorii pot dezvolta servicii web orientate pe mesaje sau pe apeluri la distanţă (RPC – Remote Procedure Call).
O invocare a unui serviciu web este realizată prin protocolul SOAP, care este bazat pe XML. Specificaţia SOAP include structura antetelor, regulile de codificare şi convenţiile pentru reprezentarea invocărilor şi răspunsurilor serviciului web. Astfel, invocările şi răspunsurile sunt transmise ca mesaje SOAP prin protocolul HTTP. Deşi mesajele SOAP sunt complexe, acest fapt este ascuns prin API-ul JAX-WS. Pe server sunt definite operaţiile serviciului web în cadrul unei interfeţe precum şi implementarea lor. Un client crează un obiect delegat (obiect local reprezentând serviciul) şi invocă metodele puse la dispoziţie de serviciul web prin intermediul acestuia. Programatorul nu trebuie să genereze mesaje SOAP şi nici să le analizeze, întrucât sistemul de execuţie JAX-WS converteşte invocările şi răspunsurile în şi din mesaje SOAP.
Avantajele JAX-WS constau în independenţa de platformă a limbajului de programare Java şi în faptul că entităţile implicate în dezvoltarea şi accesarea serviciului web nu trebuie ruleze pe o platformă Java, întrucât sunt folosite tehnologii definite de W3C: HTTP, SOAP şi WSDL. WSDL specifică un format XML pentru descrierea unui serviciu ca un set de noduri din reţea care realizează anumite operaţii asupra mesajelor.
În cele ce urmează, crearea unui serviciu web folosind tehnologia JAX-WS va fi ilustrată pe cazul particular al aplicaţii implementând funcţionalitatea unui serviciu de rezervare, al cărui comportament a fost descris pe larg în cadrul laboratorului anterior.
Un serviciu web dezvoltat cu JAX-WS este o clasă Java adnotată cu însemnarea @WebService
din pachetul javax.jws.WebService
, aceasta fiind desemnată ca entitatea care deserveşte serviciul web respectiv (eng. endpoint). Aşadar, structura prin care este descris un serviciu web (SEI – Service Endpoint Interface / Implementation) este o intefaţă sau o clasă Java unde sunt declarate metodele pe care un client le poate invoca pentru un serviciu.
endpointInterface
în adnotarea @WebService
, se poate defini explicit interfaţa, definindu-se metodele la care utilizatorul are acces.
import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebService; @WebService(serviceName = "Reservation") public class Reservation { public int numberOfSeats; public PersistentInterval timetable; public PersistentReservationData reservationData; public Reservation() { // ... } @WebMethod(operationName = "getTimeTable") public ArrayList<Interval> getTimeTable() { return persistentInterval2IntervalArrayList(timetable); } @WebMethod(operationName = "getAvailableSeats") public int getAvailableSeats(@WebParam(name = "interval") Interval interval) { int result = numberOfSeats; // ... return result; } @WebMethod(operationName = "makeReservation") public boolean makeReservation( @WebParam(name = "customerId") int customerId, @WebParam(name = "interval") Interval interval, @WebParam(name = "noOfSeatsRequired") int noOfSeatsRequired) { boolean result = false; // ... return result; } @WebMethod(operationName = "cancelReservation") public boolean cancelReservation( @WebParam(name = "customerId") int customerId, @WebParam(name = "interval") Interval interval) { boolean result = false; // ... return result; } }
Structurile (clasele, interfeţele) care definesc un serviciu web folosind tehnologia JAX-WS trebuie să îndeplinească mai multe condiţii:
javax.jws.WebService
sau javax.jws.WebServiceProvider
;endpointInterface
al însemnării @WebService
, însă acest lucru nu este obligatoriu: dacă nu este întâlnit nici un element endpointInterface
în însemnarea @WebService
, interfaţa care descrie serviciul web este definită implicit;static
sau final
;javax.jws.WebMethod
;final
şi nu trebuie să fie abstractă;finalize()
;javax.annotation.PostConstruct
sau javax.annotation.PreDestroy
pentru evenimente legate de ciclul de viaţă al serviciului web:@PreConstruct
va fi invocată înainte ca structura care implementează serviciul web să răspundă la cereri din partea clienţilor;@PreDestroy
va fi invocată înainte ca structura care defineşte serviciul web să fie îşi încheie activitatea.
În exemplul de mai sus, clasa care implementează serviciul web, Reservation
, este adnotată ca fiind descriind operaţiile acestuia prin însemnarea @WebService(serviceName=“Reservation”)
. Clasa Reservation
declară mai multe metode (getTimetable()
, getAvailableSeats()
, makeReservation()
, cancelReservation()
, getReservation()
), fiecare fiind adnotate cu @WebMethod(operationName=“”)
prin care acestea sunt făcute vizible către clienţi. Parametrii acestor metode accesibile la nivelul clienţilor trebuie să fie adnotate cu însemnarea @WebParam(name=“”)
, putându-se specifica un nume sugestiv pentru semnificaţia argumentelor. Totodată, este necesar ca implementarea să conţină şi un constructor implicit care va fi apelat atunci când un client face o invocare pentru o metodă asociată serviciului web respectiv. Pentru exemplul de faţă, este necesar ca în constructor să se construiască obiectul timetable
. Preluarea informaţiilor dintr-o sursă externă (fişier) pentru obţinerea acestui element trebuie să ţină cont de faptul că în urma operaţiei de dezvoltare (eng. deployment) a serviciului web, aplicaţia este plasată într-un context al serverului de aplicaţii care va pune la dispoziţie respectivul serviciu web, astfel încât amplasarea sursei externe trebuie să fie cunoscută ca locaţie (server web sau sistem de fişiere) în mod absolut şi nu relativ la adresa unde se găsesc clasele.
Clasa javax.xml.ws.Endpoint
pune la dispoziție mai multe metode statice prin intermediul cărora poate fi gestionat un serviciu web. Un astfel de obiect asociază o implementare a unui serviciu web, a cărui stare poate fi publicat sau nepublicat. Totodată, el încapsulează mai multe metadate, cum ar fi documentele WSDL și schemele XML, pe care le generează în funcție de adnotările din clasele ce conțin implementarea serviciului web, în cazul că acestea nu sunt furnizate explicit.
Crearea și publicarea unui obiect de tip Endpoint
se face prin metoda publică publish()
, care primește ca parametrii locația la care va fi disponibilă descrierea serviciului web (locația fișierului WSDL) precum și implementarea propriu-zisă a serviciului web:
public static Endpoint publish(String address, Object implementor) {}
Un exemplu de utilizare a acestei metode poate fi:
Endpoint.publish("http://localhost:8080/reservation/Reservation", new Reservation());
publish()
nu este blocantă, astfel încât pot fi realizate și alte operații după crearea și publicarea unui serviciu web.
WebServiceFeature
prin care se poate configura obiectul de tip Endpoint
.
WebServicePermission(“publishEndpoint”)
, în caz contrar generându-se o excepție de tip SecurityException
.
Un serviciu web care este publicat poate fi făcut indisponibil prin intermediul metodei Endpoint.stop()
.
JAXB (Java Architecture for XML Binding) oferă o modalitate de conversie între aplicaţii dezvoltate în limbajul de programare Java şi scheme XML. Poate fi folosit independent sau împreună cu JAX-WS, oferind o modalitate de transmitere a mesajelor în cazul utilizării unor servicii web. Astfel, JAX-WS deleagă către JAXB maparea tipurilor din limbajul de programare Java (pentru parametri şi valori întoarse) cu definiţii XML. Dezvoltatorii serviciilor web nu trebuie să cunoască detaliile acestei corespondenţe, însă trebuie să cunoască faptul că nu orice clasă din limbajul de programare Java poate fi folosită drept parametru sau valoare întoarsă în JAX-WS, întrucât limbajul de programare Java oferă un set de tipuri de date mult mai bogat decât schemele XML.
Conversia scheme XML – limbajul Java
|
Conversia limbajul Java – scheme XML
|
JAXB defineşte un set de adnotări astfel încât pentru clase definite de utilizator să se poată realiza conversia la un document XML:
Adnotare | Descriere |
---|---|
@XmlRootElement(namespace=“…”) | defineşte elementul rădăcină din cadrul schemei XML |
@XmlElement | face conversia între o proprietate JavaBeans sau un câmp al unei clase şi un atribut XML |
@XmlAccesorType(XMLAccessType.PACKAGE|FIELD) | este aplicat unui pachet sau unei clase Java, specificând daca proprietăţile JavaBeans sau atributele vor fi serializate |
@XmlType(propOrder={“atr1”,“atr2”,…,“atrn”}) | permite stabilirea structurii documentului XML prin indicarea ordinii în care atributele clasei vor fi copiate |
JAXB pune la dispoziţie un utilitar pentru compilarea unei scheme XML într-o clasă Java – denumit xjc
–, un utilitar de generare a unei scheme XML dintr-o clasă Java – denumit schemagen
şi un cadru general de rulare. Astfel, se poate porni de la o definiţie a unei scheme XML (XSD – XML Schema Definition), creându-se obiecte JavaBeans corespunzătoare (folosind compilatorul pentru scheme XML xjc
) sau se poate porni de la obiecte JavaBeans creându-se definiţiile unei scheme XML corespunzătoare (folosind utilitarul schemagen
).
xjc
cât şi schemagen
fac parte din distribuţia Java, începând cu versiunea 6.
După stabilirea corespondenţei dintre schema XML şi clasele Java, conversia între ele este realizată în mod automat prin intermediul JAXB. Informaţiile reţinute în cadrul documentelor XML pot fi accesate fără a fi necesară înţelegerea structurii lor. Clasele Java rezultate pot fi folosite pentru accesarea serviciului web dezvoltat. Adnotările conţin toate datele necesare pentru ca JAXB să realizeze conversia dintr-un format într-altul, astfel încât obiectele să poată fi transmise prin infrastructura de comunicaţie. JAXB asigură împachetarea obiectelor Java spre documente XML şi despachetarea documentelor XML în instanţe ale claselor Java. Totodată, JAXB poate oferi validarea documentelor XML conform schemei generate, astfel încât acestea respectă constrângerile care au fost stabilite.
Prin urmare, JAXB reprezintă tehnologia standard utilizată pentru conversia datelor în cadrul serviciilor web de dimensiuni “mari” implementate prin JAX-WS. Totuşi, JAXB poate fi folosit şi independent de JAX-WS, atunci când se doreşte gestiunea conversiei dintre documente XML şi obiecte Java în cadrul unor aplicaţii informatice.
JAXB este folosit în implementarea serviciilor web prin tehnologia JAX-WS pentru a asigura transportul obiectelor Java (parametrii sau valori întoarse ale metodelor), utilizând un format universal (XML) – independent de platformă, dar şi pentru a asigura un mecanism de persistenţă (prin documente XML), fără a face apel la JPA (Java Persistance API), adecvat stabilirii unei corespondenţe între clase Java şi baze de date relaţionale. Deşi este mai rudimentar, această soluţie este folosită atunci când starea obiectelor trebuie să fie reţinută, iar dimensiunea acestora nu este foarte voluminoasă.
În cazul exemplului de mai sus, este necesar ca obiectele cu tipul Interval să fie transportate prin infrastructura de comunicaţie, motiv pentru care definiţia clasei trebuie să indice acest lucru:
|
|
Prin folosirea adnotării @XmlAccessorType
, se indică faptul că toate atributele clasei vor fi serializate, motiv pentru care nu mai este necesară implementarea interfeţei java.io.Serializable
. Serializarea este realizată prin intermediul unor documente XML, tipul de date (specificat prin adnotarea @XmlType
) fiind Interval
/ ReservationData
. Se asigură totodată faptul că descrierea în limbaj WSDL va cuprinde definiţiile metodelor din această clasă, astfel încât ele vor putea fi folosite corespunzător în cadrul aplicaţiilor ce accesează funcţionalitatea oferită de serviciul web, fiind cuprinse în clasele generate la nivelul clientului.
De asemenea, e necesară asigurarea persistenţei pentru obiectele de tipul ArrayList<Interval>
/ ArrayList<ReservationData>
: timetable
/ reservationData
. Prin JAXB, starea acestor obiecte poate fi reţinută prin intermediul unor documente XML pe server, specificându-se un mecanism de transformare. Astfel, se vor defini clasele PersistentInterval
/ PersistentReservationData
, conţinând două atribute ArrayList<GregorianCalendar>
, respectiv două atribute ArrayList<Integer>
şi ArrayList<GregorianCalendar>
(pentru care este cunoscut mecanismul de transformare în document XML), adnotările în cadrul acestei clase (@XmlRootElement
, @XmlElement
) indicând modul cum informaţiile din obiecte vor fi transpuse în documentul XML. Trebuie definite metode setter / getter corespunzătoare pentru accesarea informaţiilor necesare.
@XmlRootElement public class PersistentInterval { private int numberOfSeats; private ArrayList<GregorianCalendar> startingIntervals; private ArrayList<GregorianCalendar> endingIntervals; @XmlElement public int getNumberOfSeats() { return numberOfSeats; } @XmlElement public ArrayList<GregorianCalendar> getStartingIntervals() { return startingIntervals; } @XmlElement public ArrayList<GregorianCalendar> getEndingIntervals() { return endingIntervals; } }
@XmlRootElement public class PersistentReservationData { private ArrayList<Integer> customerIds; private ArrayList<GregorianCalendar> startingIntervals; private ArrayList<GregorianCalendar> endingIntervals; private ArrayList<Integer> numberOfSeats; @XmlElement public ArrayList<Integer> getCustomerIds() { return customerIds; } @XmlElement public ArrayList<GregorianCalendar> getStartingIntervals() { return startingIntervals; } @XmlElement public ArrayList<GregorianCalendar> getEndingIntervals() { return endingIntervals; } @XmlElement public ArrayList<Integer> getNumberOfSeats() { return numberOfSeats; } }
Astfel un obiect de tipul ArrayList<Interval>
/ ArrayList<ReservationData>
pentru care nu era cunoscut modul de transformare în document XML este împărţit în obiecte de tipul ArrayList<GregorianCalendar>
, funcţionalitatea acestora fiind asemănătoare. Deşi modalitatea de transformare este cunoscută pentru clasa Interval
, trebuie specificat mecanismul de transformare pentru tipul de date ArrayList<Interval>
, astfel încât operaţiile de împachetare şi despachetare să fie realizate pentru obiecte de acest fel. Pentru uşurinţa accesului la datele conţinute în obiect, au fost definite două atribute corespunzătoare celor din clasa Interval
. Similar pentru ReservationData
/ ArrayList<ReservationData>
.
De remarcat faptul că adnotarea @XmlElement
va fi indicată pentru metoda getter asociată atributului respectiv şi nu pentru atributul în sine.
@XmlElement
se precizează pentru metodele getter întrucât acestea sunt publice, spre diferenţă de atributele în sine care au modul de acces privat. Într-o distribuţie anterioară a JAX-WS, adnotarea @XmlAttribute
se definea pentru atributele clasei.
Operaţiile asupra obiectelor timetable
şi reservationData
vor implica încărcarea, respectiv descărcarea lor din fişierele care le reţin starea.
JAXBContext contextPersistentInterval; JAXBContext contextPersistentReservationData; public Reservation() { try { contextPersistentInterval = JAXBContext.newInstance(PersistentInterval.class); contextPersistentReservationData = JAXBContext.newInstance(PersistentReservationData.class); } catch (Exception exception) { System.out.println("An exception has occurred: "+exception.getMessage()); if (Constants.DEBUG) exception.printStackTrace(); } if (new File("timetable.xml").exists()) { timetable = unpackPersistentInterval("timetable.xml"); numberOfSeats = timetable.getNumberOfSeats(); } else { timetable = new PersistentInterval(); packPersistentInterval(timetable, "timetable.xml"); } if (new File("reservationData.xml").exists()) { reservationData = unpackPersistentReservationData("reservationData.xml"); } else { reservationData = new PersistentReservationData(); packPersistentReservationData(reservationData,"reservationData.xml"); } }
Contextul JAXB referitor la persistenţă va fi iniţializat cu tipul de date pentru care se doreşte conversia. Obiectele timetable
şi reservationData
sunt încărcate din fişier în constructor (în condiţiile în care fişierul corespunzator există, altfel aceste obiecte sunt create împreună cu fişierele care le reţin starea), descărcarea stării în fişier urmând a fi realizată în cadrul operaţiilor makeReservation()
şi cancelReservation()
, care acţionează asupra lor.
Pentru aceasta, se vor implementa mecanismele corespunzătoare de împachetare şi despachetare puse la dispoziţie de contextul JAXB.
public final void packPersistentInterval(PersistentInterval object, String file) { try { Marshaller conversion = contextPersistentInterval.createMarshaller(); conversion.marshal(object, new FileOutputStream(file)); } catch (Exception exception) { System.out.println("An exception has occurred: "+exception.printStackTrace()); if (Constants.DEBUG) exception.printStackTrace(); } } public final void packPersistentReservationData(PersistentReservationData object, String file) { try { Marshaller conversion = contextPersistentReservationData.createMarshaller(); conversion.marshal(object, new FileOutputStream(file)); } catch (Exception exception) { System.out.println("An exception has occurred: "+exception.printStackTrace()); if (Constants.DEBUG) exception.printStackTrace(); } } public final PersistentInterval unpackPersistentInterval(String file) { try { Unmarshaller conversion = contextPersistentInterval.createUnmarshaller(); return (PersistentInterval)conversion.unmarshal(new FileInputStream(file)); } catch (Exception exception) { System.out.println("An exception has occurred: "+exception.printStackTrace()); if (Constants.DEBUG) exception.printStackTrace(); } return null; } public final PersistentReservationData unpackPersistentReservationData(String file) { try { Unmarshaller conversion = contextPersistentReservationData.createUnmarshaller(); return (PersistentReservationData)conversion.unmarshal(new FileInputStream(file)); } catch (Exception exception) { System.out.println("An exception has occurred: "+exception.printStackTrace()); if (Constants.DEBUG) exception.printStackTrace(); } return null; }
Pe baza fişierului WSDL care descrie serviciul web sunt generate clasele ce permit invocarea funcţionalităţii puse la dispoziţie de acesta. Pentru generarea unui serviciu web se foloseşte comanda wsgen
, în timp ce pentru importarea unui serviciu web (spre a putea fi invocat) se foloseşte comanda wsimport
.
wsgen
, respectiv wsimport
sunt incluse în distribuţia standard Java, începând cu versiunea 6.
wsgen [options] <SEI>
unde
options
opțiune | semnificație |
---|---|
-classpath <path> | precizează locația la care pot fi găsite clasele de intrare și extensiile |
-cp <path> | indică locația la care pot fi găsite clasele de intrare și extensiile |
-d <directory> | indică locația la care vor fi plasate fișierele de ieșire |
-encoding <encoding> | specifică mecanismul de codificare al caracterelor folosit de fișierele ce conțin codul sursă |
-extension | permite utilizarea unor extensii ale producătorului (funcționalitate care nu este cuprinsă în specificație), de exemplu -Xnocompile nu compilează clasele Java generate; totuși, utilizarea de extensii poate determina ca aplicația obțiinută să nu fie portabilă / să nu poată interacționa cu alte implementări |
-help | afișează informațiile de ajutor |
-J<javacOption> | transmite opțiunea către compilatorul javac |
-keep | determină păstrarea fișierelor generate |
-r <directory> | indică locația la care vor fi plasate resursele (de exemplu, WSDL-urile) |
-s <directory> | specifică locația la care va fi plasat codul sursă generat |
-verbose | afișeză mesaje cu privire la operațiile realizate la momentul curent |
-version | afișează informațiile legate de versiunea utilitarului |
-fullversion | afișează informațiile complete legate de versiunea utilitarului |
-wsdl[:protocol] | generează un fișier WSDLprotocol este opțional și poate avea valorile✔ soap1.1 (implicit)✔ Xsoap1.2 (nonstandard) - poate fi folosit numai împreună cu opțiunea -extension |
-inlineSchemas | include schemele XML în fișierul WSDL generat (poate fi folosit numai împreună cu opțiunea -wsdl ) |
-servicename <name> | specifică denumirea serviciului care va fi folosit în fișierul WSDL generat (poate fi folosit numai împreună cu opțiunea -wsdl ) |
-portname <name> | specifică denumirea portului care va fi folosit în fișierul WSDL generat (poate fi folosit numai împreună cu opțiunea -wsdl ) |
-x <file> | indică un descriptor XML ce conține metadate despre un serviciu web extern |
SEI
- reprezintă clasa Java conținând implementarea serviciului webExemplu:
wsgen -cp . ro.pub.cs.aipi.lab06.reservation.Reservation -wsdl -servicename {http://localhost:8080/ReservationService}Reservation
wsimport [options] <WSDL_URI>
unde
options
opțiune | semnificație |
---|---|
-b <path> | indică fișierele de asociere jaxws/jaxb sau scheme suplimentare (fiecare cale trebuie să fie prefixată de opțiunea -b ) |
-B<jaxbOption> | transmite opțiunea către compilatorul de scheme JAXB |
-catalog <file> | precizează fișierul ce conține catalogul pentru rezolvarea referințelor către entități externe (sunt suportate formatele TR9401 , XCatalog , Oasis XML ) |
-classpath <path> | precizează locația la care pot fi găsite clasele de intrare și extensiile |
-cp <path> | indică locația la care pot fi găsite clasele de intrare și extensiile |
-d <directory> | indică locația la care vor fi plasate fișierele de ieșire |
-encoding <encoding> | specifică mecanismul de codificare al caracterelor folosit de fișierele ce conțin codul sursă |
-extension | permite utilizarea unor extensii ale producătorului (funcționalitate care nu este cuprinsă în specificație); totuși, utilizarea de extensii poate determina ca aplicația obțiinută să nu fie portabilă / să nu poată interacționa cu alte implementări |
-help | afișează informațiile de ajutor |
-httpproxy:<proxy> | stabilește un proxy HTTP, având formatul [user[:password]@]proxyHost:proxyPort , unde proxyPort are valoarea implicită 8080 |
-J<javacOption> | transmite opțiunea către compilatorul javac |
-keep | determină păstrarea fișierelor generate |
-p <pkg> | indică pachetul în care va fi generat codul sursă |
-quiet | oprește afișarea mesajelor cu privire la operațiile realizate la momentul curent |
-s <directory> | specifică locația la care va fi plasat codul sursă generat |
-target <version> | generează codul sursă în funcție de versiunea specificației JAXWS indicată ✔ 2.0 ✔ 2.1 ✔ 2.2 (implicit) |
-verbose | afișeză mesaje cu privire la operațiile realizate la momentul curent |
-version | afișează informațiile legate de versiunea utilitarului |
-fullversion | afișează informațiile complete legate de versiunea utilitarului |
-wsdllocation <location> | specifică valoarea locației de unde va fi preluat fișierul WSDL conținând specificația serviciului web (parametrul @WebServiceClient.wsdlLocation ) |
-clientjar <jarfile> | creează o arhivă .jar conținând codul sursă generat împreună cu metadatele WSDL necesare pentru invocarea serviciului web |
-generateJWS | stabilește generarea unui fișier ce conține implementarea JWS |
-implDestDir <directory> | indică locația la care vor fi generate fișierele ce conțin implementarea JWS |
-implServiceName <name> | precizează denumirea locală a serviciului pentru implementarea JWS |
-implPortName <name> | precizează portul local al serviciului pentru implemnentarea JWS |
iar extensiile suportate sunt:
-XadditionalHeaders | asociează antetele care nu sunt legate de mesaje de cerere sau de răspuns la parametrii ai unor metode |
-Xauthfile | specifică fișierul care conține informațiile de autorizare în formatul http://username:password@hostname/servicename?wsdl |
-Xdebug | afișează informațiile pentru depanarea codului |
-Xno-addressing-databinding | activează asocierea tipurilor W3C EndpointReferenceType către clase din limbajul de programare Java |
-Xnocompile | determină ca fișierele Java generate să nu fie compilate |
-XdisableAuthenticator | dezactivează mecanismul de autentificare folosit de JAX-WS RI, ignorând opțiunea -Xauthfile , dacă este precizată |
-XdisableSSLHostnameVerification | dezactivează verificarea mașinilor cu privire la configurația SSL la transferul fișierelor WSDL |
WSDL_URI
- reprezintă locația fișierului WSDL care conține descrierea serviciului webExemplu:
wsimport -s . http://localhost:8080/ReservationService/Reservation?wsdl
Astfel, pentru fiecare metodă implementată de serviciul web (definită ca operation în secţiunea binding a fişierului WSDL) sunt generate clasele Operation
şi OperationResponse
, responsabile cu transmiterea cererilor și răspunsurilor prin infrastructura de comunicație:
Operation
oferă metode pentru transferul parametrilor metodelor accesibile la distanță;OperationResponse
oferă metode pentru transferul valorilor întoarse ale metodelor accesibile la distanță.
De asemenea, este furnizată și o fabrică de obiecte (ObjectFactory
) pentru crearea unor instanțe ale acestor clase.
Totodată, sunt generate şi clasele corespunzătoare obiectelor transmise ca parametrii sau rezultate întoarse, în care tipul atributelor este însă transformat astfel încât să poată fi transmise prin infrastructura de comunicaţie.
Interval
/ ReservationData
, obiectele de tip GregorianCalendar
sunt convertite în mod automat la tipul de date echivalent XMLGregorianCalendar
.
Transformările suferite de un astfel de obiect la transferul său de la server, prin infrastructura de comunicație și până la client sunt: GregorianCalendar
→ xs:dateTime
→ XMLGregorianCalendar
.
Transformările suferite de un astfel de obiect la transferul său de la clinet, prin infrastructura de comunicație și până la server sunt: XMLGregorianCalendar
→ xs:anySimpleType
→ java.lang.Object
→ GregorianCalendar
.
Invocările metodelor implementate în cadrul serviciului web se fac printr-un obiect local care funcţionează ca un delegat pentru serviciul la distanţă. Acesta este obţinut ca port al unei alte clase generate pe baza serviciului web în care sunt definite metodele, având denumirea sufixată de _Service
.
public class Client { @WebServiceRef(wsdlLocation = "META-INF/wsdl/localhost_8080/ReservationService/Reservation.wsdl") private static Reservation_Service service; public static void main(String[] args) { service = new reservation.Reservation_Service(); reservation.Reservation port = service.getReservationPort(); List<Interval> timetable = port.getTimeTable(); for (Interval timetableEntry:timetable) { System.out.println ("interval in timetable: "+timetableEntry.getStartingTime().toXMLFormat()+"->"+timetableEntry.getEndingTime().toXMLFormat()); } } }
Prin adnotarea @WsdlServiceRef
se indică fișierul WSDL care conține descrierea serviciului web referit, acesta trebuind să existe la locația specificată de parametrul wsdlLocation
.
wsdlLocation
poate referi și un fișier WSDL existent pe un server web, de exemplu: http://localhost:8080/ReservationService/Reservation?wsdl.
Pe baza acestei descrieri, vor fi generate în mod automat clasele responsabile cu transportul parametrilor și valorilor întoarse ale metodelor accesibile la distanță.
În situația în care există mai multe servicii web care pun la dispoziție aceeași funcționalitate (aceleași metode accesibile la distanță), dar cu implementări diferite, există posibilitatea invocării dinamice (în momentul execuției), serviciul fiind obținut pe baza metodei statice create()
din cadrul clasei Service
, primind ca parametrii denumirea sa și locația la care este disponibil documentul WSDL ce îi descrie funcționalitatea:
public static Service create(URL wsdlDocumentLocation, QName serviceName) throws javax.xml.ws.WebServiceException {}
Un exemplu de instanțiere a unui astfel de serviciu ar putea fi:
try { URL wsdlDocumentLocation = new URL("http://localhost:8080/reservation/Reservation?wsdl"); QName serviceName = new QName("http://reservation.lab06.aipi.cs.pub.ro/Reservation", "Reservation"); Service service = Service.create(wsdlDocumentLocation, serviceName); } catch (MalformedURLException malformedURLException) { System.out.println("An exception had occurred: "+malformedURLException.getMessage()); if (Constants.DEBUG) malformedURLException.printStackTrace(); }
Ulterior, funcționalitatea îi poate fi invocată prin intermediul metodei getPort()
a obiectului de tip Service
, care primește ca parametru clasa pe care o reprezintă (la care va fi convertit):
public <T> T getPort(Class<T> serviceEndpointInterface) throws javax.xml.ws.WebServiceException {}
Un exemplu de obținere a unui port pentru care pot fi invocate ulterior serviciile pot fi:
Reservation port = service.getPort(Reservation.class);
Se creează un proiect de tip Java Web → Web Application.
În situația în care Java Enterprise Edition nu este inițializată pentru NetBeans, această operație este realizată în acest moment, putând dura, după caz, o perioadă de timp mai mare.
Pentru aplicația web se precizează o denumire, precum și locația unde va fi amplasat proiectul corespunzător.
Este indicat tipul de server de aplicații care va fi utilizat (GlassFish Server 4.1), versiunea Java Enterprise Edition (în cazul de față Java EE 7 Web), precum și contextul din care va putea fi accesat serviciul web (/ReservationService
).
Pot fi specificate și alte tehnologii cu care să fie integrat serviciul web (Spring Web MVC, Java Server Faces, Struts, Hibernate).
Proiectul va conține o resursă de tip Web Service…, accesibilă din meniul contextual prin selectarea opțiunii New.
Pentru serviciul web trebuie completate atribute precum:
După implementarea funcţionalităţii puse la dispoziţie, aceasta poate fi vizualizată din modul Design.
Sunt indicate aici numele şi tipul parametrilor acceptaţi de metodele serviciului web, tipul rezultatului întors, excepţiile care pot fi generate precum şi o descriere a funcţionalităţii respective.
Modulul permite gestiunea operațiilor pe care serviciul web le pune la dispoziția utilizatorilor (adăugare, modificare, ștergere) precum și controlul unor atribute ce țin de calitatea serviciului:
În momentul în care este lansat în execuţie serviciul web (opţiunea Deploy din meniul contextual corespunzător proiectului), este pornit şi serverul de aplicaţii GlassFish.
Ulterior, modificările asupra codului sursă sunt reflectate la nivelul serverului de aplicaţii în mod automat atunci când acesta este salvat (codul sursă este compilat, iar clasele sunt plasate în contextul serverului GlassFish).
Accesând consola serverului de aplicaţii GlassFish (la adresa http://localhost:4848), poate fi vizualizat serviciul web dezvoltat în secţiunea Applications sub denumirea care a fost indicată la crearea lui.
admin
, iar parola asociată acestuia este vidă.
Legătura ViewEndpoint corespunzătoare componentei serviciului web trimite către fişierul WSDL care conţine descrierea serviciului web (la adresa http://localhost:8080/ReservationService/Reservation?wsdl), permiţând şi testarea funcţionalităţii acestuia.
<?xml version="1.0" encoding="UTF-8"?> <!-- Published by JAX-WS RI (http://jax-ws.java.net). RI's version is Metro/2.3.1-b419 (branches/2.3.1.x-7937; 2014-08-04T08:11:03+0000) JAXWS-RI/2.2.10-b140803.1500 JAXWS-API/2.2.11 JAXB-RI/2.2.10-b140802.1033 JAXB-API/2.2.12-b140109.1041 svn-revision#unknown. --> <!-- Generated by JAX-WS RI (http://jax-ws.java.net). RI's version is Metro/2.3.1-b419 (branches/2.3.1.x-7937; 2014-08-04T08:11:03+0000) JAXWS-RI/2.2.10-b140803.1500 JAXWS-API/2.2.11 JAXB-RI/2.2.10-b140802.1033 JAXB-API/2.2.12-b140109.1041 svn-revision#unknown. --> <definitions xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsp1_2="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://reservation.lab06.aipi.cs.pub.ro/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://reservation.lab06.aipi.cs.pub.ro/" name="Reservation"> <types> <xsd:schema> <xsd:import namespace="http://reservation.lab06.aipi.cs.pub.ro/" schemaLocation="http://andrei-pc:8080/ReservationService/Reservation?xsd=1"/> </xsd:schema> </types> <message name="getAvailableSeats"> <part name="parameters" element="tns:getAvailableSeats"/> </message> <message name="getAvailableSeatsResponse"> <part name="parameters" element="tns:getAvailableSeatsResponse"/> </message> <message name="cancelReservation"> <part name="parameters" element="tns:cancelReservation"/> </message> <message name="cancelReservationResponse"> <part name="parameters" element="tns:cancelReservationResponse"/> </message> <message name="getTimeTable"> <part name="parameters" element="tns:getTimeTable"/> </message> <message name="getTimeTableResponse"> <part name="parameters" element="tns:getTimeTableResponse"/> </message> <message name="makeReservation"> <part name="parameters" element="tns:makeReservation"/> </message> <message name="makeReservationResponse"> <part name="parameters" element="tns:makeReservationResponse"/> </message> <message name="getReservations"> <part name="parameters" element="tns:getReservations"/> </message> <message name="getReservationsResponse"> <part name="parameters" element="tns:getReservationsResponse"/> </message> <portType name="Reservation"> <operation name="getAvailableSeats"> <input wsam:Action="http://reservation.lab06.aipi.cs.pub.ro/Reservation/getAvailableSeatsRequest" message="tns:getAvailableSeats"/> <output wsam:Action="http://reservation.lab06.aipi.cs.pub.ro/Reservation/getAvailableSeatsResponse" message="tns:getAvailableSeatsResponse"/> </operation> <operation name="cancelReservation"> <input wsam:Action="http://reservation.lab06.aipi.cs.pub.ro/Reservation/cancelReservationRequest" message="tns:cancelReservation"/> <output wsam:Action="http://reservation.lab06.aipi.cs.pub.ro/Reservation/cancelReservationResponse" message="tns:cancelReservationResponse"/> </operation> <operation name="getTimeTable"> <input wsam:Action="http://reservation.lab06.aipi.cs.pub.ro/Reservation/getTimeTableRequest" message="tns:getTimeTable"/> <output wsam:Action="http://reservation.lab06.aipi.cs.pub.ro/Reservation/getTimeTableResponse" message="tns:getTimeTableResponse"/> </operation> <operation name="makeReservation"> <input wsam:Action="http://reservation.lab06.aipi.cs.pub.ro/Reservation/makeReservationRequest" message="tns:makeReservation"/> <output wsam:Action="http://reservation.lab06.aipi.cs.pub.ro/Reservation/makeReservationResponse" message="tns:makeReservationResponse"/> </operation> <operation name="getReservations"> <input wsam:Action="http://reservation.lab06.aipi.cs.pub.ro/Reservation/getReservationsRequest" message="tns:getReservations"/> <output wsam:Action="http://reservation.lab06.aipi.cs.pub.ro/Reservation/getReservationsResponse" message="tns:getReservationsResponse"/> </operation> </portType> <binding name="ReservationPortBinding" type="tns:Reservation"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/> <operation name="getAvailableSeats"> <soap:operation soapAction=""/> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> </operation> <operation name="cancelReservation"> <soap:operation soapAction=""/> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> </operation> <operation name="getTimeTable"> <soap:operation soapAction=""/> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> </operation> <operation name="makeReservation"> <soap:operation soapAction=""/> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> </operation> <operation name="getReservations"> <soap:operation soapAction=""/> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> </operation> </binding> <service name="Reservation"> <port name="ReservationPort" binding="tns:ReservationPortBinding"> <soap:address location="http://andrei-pc:8080/ReservationService/Reservation"/> </port> </service> </definitions>
Se creează un proiect de tip Java Desktop Application care va conține un client al unui serviciu web (New → Web Service Client din meniul contextual).
Se indică locația (URL-ul) la care se găsește specificația WSDL a serviciului web (http://localhost:8080/ReservationService/Reservation?wsdl), pe baza căreia vor fi generate clasele care descriu operațiile ce pot fi invocate.
Clasele generate sunt vizibile în secțiunea Generated Sources (jax-ws).
Operaţiile pot fi testate din consola serverului de aplicaţii GlassFish, disponibile la http://localhost:8080/ReservationService/Reservation?Tester. Invocarea unei funcţionalităţi în acest caz este întotdeauna însoţită de mesajele SOAP care sunt schimbate între client şi server, fiind descrise cererea (invocarea metodei din cadrul serviciului web) precum şi răspunsul (rezultatul execuţiei operaţiei respective în contextul serverului unde rulează serviciul web).
Pentru exemplul considerat, în cazul metodelor getTimetable()
, getReservation()
pot fi vizualizate referinţe (adrese) către obiecte de tip Interval
/ ReservationData
, în timp ce metodele getAvailableSeats()
şi makeReservation()
/ cancelReservation()
nu pot fi accesate corespunzător întrucât ar trebui specificaţi ca parametrii referinţe către obiecte de tip Interval
, existente în contextul serverului pe care se află serviciul web.
Pentru operaţia getTimetable()
, formatul mesajelor SOAP interschimbate între client şi server este următorul:
<?xml version="1.0" encoding="UTF-8"?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Header/> <S:Body> <ns2:getTimeTable xmlns:ns2="http://reservation.lab06.aipi.cs.pub.ro/"/> </S:Body> </S:Envelope>
<?xml version="1.0" encoding="UTF-8"?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Header/> <S:Body> <ns2:getTimeTableResponse xmlns:ns2="http://reservation.lab06.aipi.cs.pub.ro/"> <return> <startingTime>2014-12-17T10:00:00+02:00</startingTime> <endingTime>2014-12-17T12:00:00+02:00</endingTime> </return> <return> <startingTime>2014-12-17T14:00:00+02:00</startingTime> <endingTime>2014-12-17T20:00:00+02:00</endingTime> </return> <return> <startingTime>2014-12-20T14:00:00+02:00</startingTime> <endingTime>2014-12-20T16:00:00+02:00</endingTime> </return> <return> <startingTime>2014-12-20T18:00:00+02:00</startingTime> <endingTime>2014-12-20T20:00:00+02:00</endingTime> </return> </ns2:getTimeTableResponse> </S:Body> </S:Envelope>
Pentru operaţia getReservations()
, primind ca parametru un identificator de client egal cu 1, formatul mesajelor SOAP interschimbate între client şi server este următorul:
<?xml version="1.0" encoding="UTF-8"?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Header/> <S:Body> <ns2:getReservations xmlns:ns2="http://reservation.lab06.aipi.cs.pub.ro/"> <customerId>1</customerId> </ns2:getReservations> </S:Body> </S:Envelope>
<?xml version="1.0" encoding="UTF-8"?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Header/> <S:Body> <ns2:getReservationsResponse xmlns:ns2="http://reservation.lab06.aipi.cs.pub.ro/"> <return> <clientId>1</clientId> <interval> <startingTime>2014-12-17T14:00:00+02:00</startingTime> <endingTime>2014-12-17T18:00:00+02:00</endingTime> </interval> <numberOfSeats>10</numberOfSeats> </return> </ns2:getReservationsResponse> </S:Body> </S:Envelope>
PersistentInterval
și PersistentReservationData
(timetable.xml
, respectiv reservationData.xml
din directorul output
), astfel încât orice operație care realizează modificări trebuie să asigure și sincronizarea cu informațiile de pe discul local.
0. [0 puncte] Să se plaseze în contextul serverului de aplicații:
input
cu fișierul timetable.txt
;output
(inițial acesta este gol).~/glassfish-4.1/glassfish/domains/domain1/config
(dacă mediul de dezvoltare a fost instalat folosind utilizatorul root
, acesta va fi plasat în /usr/local
)%USERPROFILE%\AppData\Roaming\NetBeans\8.0.1\config\GF_4.1\domain1\config
1. [10 puncte] Să se încarce, în constructorul clasei Reservation
(din pachetul ro.pub.cs.aipi.lab06.reservation
), orarul de funcționare al programului de rezervare, în condițiile în care pe server nu există un fișier XML care conține aceste informații.
Obținerea programului de funcționare se realizează prin parsarea fișierului timetable.txt
din directorul input
, acesta urmând a fi încărcat în atributul timetable
. De asemenea, va fi încărcată și valoarea atributului numberOfSeats
.
2. [10 puncte] Să se implementeze metodele de conversie între tipurile de date ArrayList<Interval>
şi PersistentInterval
, respectiv ArrayList<ReservationData>
şi PersistentReservationData
.
Acestea sunt tipurile de date corespunzătoare atributelor clasei Reservation
, prin intermediul cărora se realizează persistența între invocări succesive ale metodelor la distanță, respectiv tipurile de date prin care aceste informații sunt accesate / sunt făcute vizibile la distanță, prin intermediul metodelor implementate.
PersistentInterval
→ ArrayList<Interval>
şi PersistentReservationData
→ ArrayList<ReservationData>
.
3. [10 puncte] Să se implementeze metoda getTimeTable()
.
4. [40 puncte] Să se implementeze metoda getAvailableSeats()
.
5. [10 puncte] Să se implementeze metoda makeReservation()
.
6. [10 puncte] Să se implementeze metoda cancelReservation()
.
7. [0 puncte]
Să se lanseze în execuţie ReservationService
accesând opțiunea Run as… → Java Application din meniul contextual asociat proiectului.
Să se lanseze în execuţie ReservationService
accesând opțiunea Deploy din meniul contextual asociat proiectului.
8. [0 puncte]
Să se genereze sursele pentru client, rulând scriptul client.[bat|sh]
din directorul scripts
.
Să se genereze sursele pentru client, accesând opțiunea Refresh din meniul contextual asociat referinței serviciului web (Web Service References → Reservation (right-click)).
9. [10 puncte] Să se apeleze metodele makeReservation()
şi cancelReservation()
din client.
GregorianCalendar
din cadrul clasei Interval
va putea fi afişat apelând metoda toXMLFormat()
.
Interval
, motiv pentru carea un atribut GregorianCalendar
al acestuia, desemnând una dintre limite, va fi specificat prin metodele de tip setter corespunzătoare, acesta fiind creat prin metoda DatatypeFactory.newInstance().newXMLGregorianCalendar(new GregorianCalendar(year, month, day_of_month, hour_of_day, minute)
care furnizează un obiect de tip XMLGregorianCalendar
corespunzător tipului de dată care poate fi transmis prin infrastructura de comunicație (pentru care există mapare într-o schemă XML).
10. [10 puncte] Să se implementeze o nouă metodă în serviciul web, denumită getReservations()
care întoarce rezervările realizate de un anumit client, al cărui identificator este precizat ca parametru al metodei.
@WebMethod(operationName = "getReservations") public ArrayList<ReservationData> getReservations(@WebParam(name = "customerId") int customerId);
Eric Jendrock, Ricardo Cervera-
Navarro, Ian Evans, Kim Haase, William Markito - The Java EE 7 Tutorial - capitolele 27, 28
Getting Started with JAX-WS Web Services
Java Architecture for XML Binding (JAXB)
JAXB - Tutorial