====== Laborator 07 ======
====== Construirea unui serviciu web folosind tehnologia JAX-WS ======
===== Obiective ====
* descrierea tipurilor de servicii web ce pot fi dezvoltate folosind limbajul de programare Java și identificarea oportunității folosirii acestora;
* înţelegerea arhitecturii pe care o oferă tehnologia JAX-WS pentru dezvoltarea unui serviciu web, precum şi mecanismul prin care sunt împachetate obiectele spre a fi transmise prin infrastructura de comunicaţie;
* proiectarea unor structuri de date (clase) care să poată fi stocate sub formă de scheme XML;
* dezvoltarea unui serviciu web folosind tehnologia JAX-WS care să pună la dispoziție mai multe funcționalități la distanță, asigurând totodată persistența informațiilor;
* folosirea unui serviciu web dezvoltat prin tehnologia JAX-WS prin invocarea metodelor implementate;
* utilizarea unui server de aplicații care să găzduiască serviciul web.
===== Cuvinte Cheie ====
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
===== Materiale Ajutătoare =====
TODO
===== Tipuri de servicii web și implementarea lor folosind Java ======
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:
* o interfaţă care descrie operaţiile pe care serviciul web le pune la dispoziţia utilizatorilor; se poate folosi WSDL pentru detalii cu privire la mesaje, operaţii, corespondenţe şi locaţia de la care se poate accesa serviciul web;
Mesajele SOAP pot fi procesate prin tehnologia JAX-WS și fără a se pune la dispoziția utilizatorilor specificația WSDL.
* arhitectura serviciului web trebuie să răspundă unor cerinţe complexe nonfuncţionale (spre exemplu: procesarea unor tranzacții, rezolvarea unor aspecte ce vizează securitatea, probleme de adresare, coordonare, încredere), stabilind o sintaxă pentru acestea;
* atât procesarea cât şi invocarea operaţiilor trebuie să se facă asincron; există standarde care oferă suport pentru această infrastructură, cum ar fi WSRM (Web Services Reliable Messaging) sau API-uri ca JAX-WS.
**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:
* performanţa poate fi îmbunătăţită printr-o infrastructură de stocare (//eng.// caching system) atâta timp cât informaţiile oferite de serviciul web nu sunt generate în mod dinamic;
Implementarea infrastructurii de stocare trebuie să se facă ținând cont de limitările metodei HTTP GET, pentru cele mai multe dintre serverele de aplicații.
* atât clientul cât şi serverul au o înţelegere (comună) cu privire la contextul şi conţinutul care este transmis în procesul de interacţiune dintre ele, întrucât nu există o interfaţă care să descrie (standardizat) informaţiile care sunt transmise;
De regulă, producătorii de servicii web fără stare pun la dispoziţie şi unelte care descriu utilizatorilor interfaţa acestora în majoritatea limbajelor de programare folosite pe scară largă.
* lăţimea de bandă limitată este un aspect care trebuie luat în considerare întrucât serviciile web fără stare sunt mai frecvent folosite pentru dispozitive cum ar fi telefoane mobile sau PDA-uri; în această situaţie surplusul de informaţii conţinut de mesajele bazate pe XML (obiectele SOAP) limitează performanţa, implicând şi costuri mai ridicate;
* integrarea cu diferite aplicaţii Internet se poate realiza cu uşurinţă folosind tehnologii precum JAX-RS sau AJAX (Asynchronous JavaScript with XML) ca şi utilitare ca DWR (Direct Web Remoting) spre a invoca serviciile dezvoltate; fiind expuse prin XML, serviciile web pot fi utilizate fără modificarea semnificativă a arhitecturii paginii Internet.
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 - aspecte generale =====
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.
{{ :laboratoare:laborator07:01JAXWSarchitecture.png?nolink&500 }}
===== Proiectarea serviciului web =====
Un serviciu web dezvoltat cu JAX-WS este o clasă Java adnotată cu însemnarea [[https://docs.oracle.com/javaee/7/api/javax/jws/WebService.html|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.
Nu este obligatoriu să se definească o interfaţă (explicit) atunci când serviciul web este construit cu JAX-WS, întrucât funcţionalitatea sa este descrisă prin implementarea operaţiilor. Totuşi, folosind elementul ''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 = "BookManager")
public class BookManager extends PersistentEntityManager implements BookManagerInterface {
public BookManager() {
super(Book.class);
}
@WebMethod(operationName = "getBooksList")
@Override
public List getBooksList() {
List result = new ArrayList<>();
// ...
return result;
}
@WebMethod(operationName = "getBooksListWithStockpile")
@Override
public List getBooksListWithStockpile(@WebParam(name = "stockpile") int stockpile) {
List result = new ArrayList<>();
// ...
return result;
}
}
Structurile (clasele, interfeţele) care definesc un serviciu web folosind tehnologia JAX-WS trebuie să îndeplinească mai multe condiţii:
* clasa care implementează serviciul web trebuie să fie adnotată cu însemnările [[https://docs.oracle.com/javaee/7/api/javax/jws/WebService.html|javax.jws.WebService]] sau [[http://docs.oracle.com/javaee/7/api/javax/xml/ws/spi/Provider.html|javax.xml.ws.spi.Provider]];
* clasa care implementează serviciul web poate referi în mod explicit interfaţa care descrie acest serviciu web prin elementul ''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;
* metodele ce implementează funcţionalitatea serviciului web trebuie să fie publice şi nu trebuie să fie declarate ca fiind ''static'' sau ''final'';
* metodele ce implementează funcţionalitatea serviciului web, ce sunt accesibile utilizatorilor trebuie să fie adnotate cu însemnarea [[https://docs.oracle.com/javaee/7/api/javax/jws/WebMethod.html|javax.jws.WebMethod]];
* metodele ce implementează funcţionalitatea serviciului web trebuie să aibă parametrii şi rezultate întoarse de tipuri compatibile cu JAXB; acestea pot fi adnotate cu însemnările [[https://docs.oracle.com/javaee/7/api/javax/jws/WebParam.html|javax.jws.WebParam]], respectiv [[https://docs.oracle.com/javaee/7/api/javax/jws/WebResult.html|javax.jws.WebResult]];
* clasa care implementează serviciul web nu trebuie să fie declarată ca fiind ''final'' şi nu trebuie să fie abstractă;
* clasa care implementează serviciul web trebuie să implementeze un constructor public implicit;
* clasa care implementează serviciul web nu trebuie să conţină metoda ''finalize()'';
* clasa care implementează serviciul web poate folosi adnotaţiile [[http://docs.oracle.com/javaee/7/api/javax/annotation/PostConstruct.html|javax.annotation.PostConstruct]] sau [[http://docs.oracle.com/javaee/7/api/javax/annotation/PreDestroy.html|javax.annotation.PreDestroy]] pentru evenimente legate de ciclul de viaţă al serviciului web:
* metoda ''@PreConstruct'' va fi invocată înainte ca structura care implementează serviciul web să răspundă la cereri din partea clienţilor;
* metoda ''@PreDestroy'' va fi invocată înainte ca structura care defineşte serviciul web să fie îşi încheie activitatea.
În exemplu, clasa care implementează serviciul web, ''BookManager'', este adnotată ca fiind un serviciu web, descriind operaţiile acestuia prin însemnarea ''@WebService(serviceName="BookManager")''. Clasa ''BookManager'' declară mai multe metode (''getBooksList()'', ''getBooksListWithStockpile()''), 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. 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 [[http://docs.oracle.com/javaee/7/api/javax/xml/ws/Endpoint.html|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 una dintre implementările metodei publice [[http://docs.oracle.com/javaee/7/api/javax/xml/ws/Endpoint.html#publish-java.lang.String-java.lang.Object-|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/bookstore/BookManager", new BookManager());
Metoda ''publish()'' nu este blocantă, astfel încât pot fi realizate și alte operații după crearea și publicarea unui serviciu web.Opțional, metoda poate primi niște parametri de tip [[http://docs.oracle.com/javaee/7/api/javax/xml/ws/WebServiceFeature.html|javax.xml.ws.WebServiceFeature]] prin care se poate configura obiectul de tip ''Endpoint''.În situația în care se folosește un sistem de gestiune a securității, este necesar ca aplicația să dețină permisiunea ''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 [[http://docs.oracle.com/javaee/7/api/javax/xml/ws/Endpoint.html#stop--|stop()]] a clasei ''Endpoint''.
===== Utilizarea JAXB pentru conversia dintre clase Java și scheme XML =====
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
^ tip schemă XML ^ tip Java ^
| ''xsd:string'' | ''java.lang.String'' |
| ''xsd:integer'' | ''java.math.BigInteger'' |
| ''xsd:int'' | ''int'' |
| ''xsd:long'' | ''long'' |
| ''xsd:short'' | ''short'' |
| ''xsd:decimal'' | ''java.math.BigDecimal'' |
| ''xsd:float'' | ''float'' |
| ''xsd:double'' | ''double'' |
| ''xsd:boolean'' | ''boolean'' |
| ''xsd:byte'' | ''byte'' |
| ''xsd:QName'' | ''javax.xml.namespace.QName'' |
| ''xsd:dateTime'' | ''javax.xml.datatype.XMLGregorianCalendar'' |
| ''xsd:base64Binary'' | ''byte[]'' |
| ''xsd:hexBinary'' | ''byte[]'' |
| ''xsd:unsignedInt'' | ''long'' |
| ''xsd:unsignedShort'' | ''int'' |
| ''xsd:unsignedByte'' | ''short'' |
| ''xsd:time'' | ''javax.xml.datatype.XMLGregorianCalendar'' |
| ''xsd:date'' | ''javax.xml.datatype.XMLGregorianCalendar'' |
| ''xsd:g'' | ''javax.xml.datatype.XMLGregorianCalendar'' |
| ''xsd:anySimpleType'' | ''java.lang.Object'' |
| ''xsd:anySimpleType'' | ''java.lang.String'' |
| ''xsd:duration'' | ''javax.xml.datatype.Duration'' |
| ''xsd:NOTATION'' | ''javax.xml.namespace.QName'' |
Conversia limbajul Java – scheme XML
^ tip Java ^ tip schemă XML ^
| ''java.lang.String'' | ''xs:string'' |
| ''java.math.BigInteger'' | ''xs:integer'' |
| ''java.math.BigDecimal'' | ''xs:decimal'' |
| ''java.util.Calendar'' | ''xs:dateTime'' |
| ''java.util.Date'' | ''xs:dateTime'' |
| ''javax.xml.namespace.QName'' | ''xs:QName'' |
| ''java.net.URI xs:string'' | ''xs:string'' |
| ''javax.xml.datatype.XMLGregorianCalendar'' | ''xs:anySimpleType'' |
| ''javax.xml.datatype.Duration'' | ''xs:duration'' |
| ''java.lang.Object'' | ''xs:anyType'' |
| ''java.awt.Image'' | ''xs:base64Binary'' |
| ''javax.activation.DataHandler'' | ''xs:base64Binary'' |
| ''javax.xml.transform.Source'' | ''xs:base64Binary'' |
| ''java.util.UUID'' | ''xs:string'' |
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 ^
| [[https://docs.oracle.com/javase/7/docs/api/javax/xml/bind/annotation/XmlRootElement.html|@XmlRootElement(namespace="...")]]| defineşte elementul rădăcină din cadrul schemei XML |
| [[https://docs.oracle.com/javase/7/docs/api/javax/xml/bind/annotation/XmlElement.html|@XmlElement]] | face conversia între o proprietate JavaBeans sau un câmp al unei clase şi un atribut XML |
| [[https://docs.oracle.com/javase/7/docs/api/javax/xml/bind/annotation/XmlAccessorType.html|@XmlAccesorType(XMLAccessType.PACKAGE|FIELD)]] | este aplicat unui pachet sau unei clase Java, specificând daca proprietăţile JavaBeans sau atributele vor fi serializate |
| [[https://docs.oracle.com/javase/7/docs/api/javax/xml/bind/annotation/XmlType.html|@XmlType(propOrder={"atr_1","atr_2",...,"atr_n"})]] | 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'').
Atât ''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.
{{ :laboratoare:laborator07:02JAXBarchitecture.png?nolink&800 }}
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 ''BookInformation'' respectiv ''BookInformationDetailed'' să fie transportate prin infrastructura de comunicaţie, motiv pentru care definiţia clasei trebuie să indice acest lucru:
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import ro.pub.cs.aipi.lab07.entities.PersistentEntity;
@XmlRootElement
@XmlType(name = "BookInformation")
public class BookInformation extends PersistentEntity {
protected Long id;
protected String title;
protected String writers;
protected String collection;
protected String publishingHouse;
protected Long edition;
protected Long printingYear;
protected String country;
public BookInformation() {
}
public BookInformation(Long id, String title, String writers, String collection, String publishingHouse,
Long edition, Long printingYear, String country) {
this.id = id;
this.title = title;
this.writers = writers;
this.collection = collection;
this.publishingHouse = publishingHouse;
this.edition = edition;
this.printingYear = printingYear;
this.country = country;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@XmlElement
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@XmlElement
public String getWriters() {
return writers;
}
public void setWriters(String writers) {
this.writers = writers;
}
@XmlElement
public String getCollection() {
return collection;
}
public void setCollection(String collection) {
this.collection = collection;
}
@XmlElement
public String getPublishingHouse() {
return publishingHouse;
}
public void setPublishingHouse(String publishingHouse) {
this.publishingHouse = publishingHouse;
}
@XmlElement
public Long getEdition() {
return edition;
}
public void setEdition(Long edition) {
this.edition = edition;
}
@XmlElement
public Long getPrintingYear() {
return printingYear;
}
public void setPrintingYear(Long printingYear) {
this.printingYear = printingYear;
}
@XmlElement
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
}
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import ro.pub.cs.aipi.lab07.entities.PersistentEntity;
@XmlRootElement
@XmlType(name = "BookInformationDetailed")
public class BookInformationDetailed extends PersistentEntity {
protected Long id;
protected String title;
protected String subtitle;
protected String writers;
protected String categories;
protected String formats;
protected String languages;
public BookInformationDetailed() {
}
public BookInformationDetailed(Long id, String title, String subtitle, String writers, String categories,
String formats, String languages) {
this.id = id;
this.title = title;
this.subtitle = subtitle;
this.writers = writers;
this.categories = categories;
this.formats = formats;
this.languages = languages;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@XmlElement
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@XmlElement
public String getSubtitle() {
return subtitle;
}
public void setSubtitle(String subtitle) {
this.subtitle = subtitle;
}
@XmlElement
public String getWriters() {
return writers;
}
public void setWriters(String writers) {
this.writers = writers;
}
@XmlElement
public String getCategories() {
return categories;
}
public void setCategories(String categories) {
this.categories = categories;
}
@XmlElement
public String getFormats() {
return formats;
}
public void setFormats(String formats) {
this.formats = formats;
}
@XmlElement
public String getLanguages() {
return languages;
}
public void setLanguages(String languages) {
this.languages = languages;
}
}
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 ''BookInformation'' / ''BookInformationDetailed''. 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 remarcat faptul că adnotarea ''@XmlElement'' va fi indicată pentru metoda getter asociată atributului respectiv şi nu pentru atributul în sine.
Adnotarea ''@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 [[https://docs.oracle.com/javase/7/docs/api/javax/xml/bind/annotation/XmlAttribute.html|@XmlAttribute]] se definea pentru atributele clasei.
Pot exista situații în care instanțe ale obiectelor serializabile, implicate în metodele expuse sub formă de servicii web ca parametri sau valori întoarse să trebuiască să fie salvate / încărcate pe server sub forma unor fișiere XML, între invocări succesive ale acestora. O astfel de funcționalitate poate fi necesară în situația în care este utilizat un server de aplicații care nu menține obiectul asociat serviciului web și nici proprietățile pe care acesta le conține (acesta menținând o stare intermediară). În acest scop pot fi utilizate funcționalitățile puse la dispoziție de clasa [[http://docs.oracle.com/javaee/7/api/javax/xml/bind/JAXBContext.html|javax.xml.bind.JAXBContext]]. Aceste obiecte sunt de regulă instanțiate pe constructorul clasei care implementează metodele serviciului web, folosindu-se una dintre implementările metodei statice [[http://docs.oracle.com/javaee/7/api/javax/xml/bind/JAXBContext.html#newInstance-java.lang.Class...-|newInstance()]].
JAXBContext contextBookInformation;
JAXBContext contextBookInformationDetailed;
public BookManager() {
try {
contextBookInformation = JAXBContext.newInstance(BookInformation.class);
contextBookInformationDetailed = JAXBContext.newInstance(BookInformationDetailed.class);
} catch (Exception exception) {
System.out.println("An exception has occurred: " + exception.getMessage());
if (Constants.DEBUG) {
exception.printStackTrace();
}
}
// ...
}
Operațiile de salvare / încărcare în / din fișier sunt realizate prin intermediul claselor [[http://docs.oracle.com/javaee/7/api/javax/xml/bind/Marshaller.html|javax.xml.bind.Marshaller]], respectiv [[http://docs.oracle.com/javaee/7/api/javax/xml/bind/Unmarshaller.html|javax.xml.bind.Unmarshaller]], instanțe ale acestora fiind furnizate prin intermediul metodelor [[http://docs.oracle.com/javaee/7/api/javax/xml/bind/Marshaller.html#marshal-java.lang.Object-java.io.OutputStream-|marshal()]] și [[http://docs.oracle.com/javaee/7/api/javax/xml/bind/Unmarshaller.html#unmarshal-org.xml.sax.InputSource-|unmarshal()]].
public final void packBookInformation(BookInformation object, String file) {
try {
Marshaller conversion = contextBookInformation.createMarshaller();
conversion.marshal(object, new FileOutputStream(file));
} catch (Exception exception) {
System.out.println("An exception has occurred: " + exception.getMessage());
if (Constants.DEBUG) {
exception.printStackTrace();
}
}
}
public final void packBookInformationDetailed(BookInformationDetailed object, String file) {
try {
Marshaller conversion = contextBookInformationDetailed.createMarshaller();
conversion.marshal(object, new FileOutputStream(file));
} catch (Exception exception) {
System.out.println("An exception has occurred: " + exception.getMessage());
if (Constants.DEBUG) {
exception.printStackTrace();
}
}
}
public final BookInformation unpackBookInformation(String file) {
try {
Unmarshaller conversion = contextBookInformation.createUnmarshaller();
return (BookInformation)conversion.unmarshal(new FileInputStream(file));
} catch (Exception exception) {
System.out.println("An exception has occurred: " + exception.getMessage());
if (Constants.DEBUG) {
exception.printStackTrace();
}
}
return null;
}
public final BookInformationDetailed unpackBookInformationDetailed(String file) {
try {
Unmarshaller conversion = contextBookInformationDetailed.createUnmarshaller();
return (BookInformationDetailed)conversion.unmarshal(new FileInputStream(file));
} catch (Exception exception) {
System.out.println("An exception has occurred: " + exception.getMessage());
if (Constants.DEBUG) {
exception.printStackTrace();
}
}
return null;
}
===== Interogarea unui serviciu web =====
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''.
Utilitarele ''wsgen'', respectiv ''wsimport'' sunt incluse în distribuţia standard Java, începând cu versiunea 6.
wsgen [options]
unde
* ''options''
^ opțiune ^ semnificație ^
| ''-classpath '' | precizează locația la care pot fi găsite clasele de intrare și extensiile |
| ''-cp '' | indică locația la care pot fi găsite clasele de intrare și extensiile |
| ''-d '' | indică locația la care vor fi plasate fișierele de ieșire |
| ''-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'' | transmite opțiunea către compilatorul ''javac'' |
| ''-keep'' | determină păstrarea fișierelor generate |
| ''-r '' | indică locația la care vor fi plasate resursele (de exemplu, WSDL-urile) |
| ''-s '' | 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 WSDL\\ ''protocol'' 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 '' | specifică denumirea serviciului care va fi folosit în fișierul WSDL generat (poate fi folosit numai împreună cu opțiunea ''-wsdl'') |
| ''-portname '' | specifică denumirea portului care va fi folosit în fișierul WSDL generat (poate fi folosit numai împreună cu opțiunea ''-wsdl'') |
| ''-x '' | indică un descriptor XML ce conține metadate despre un serviciu web extern |
* ''SEI'' - reprezintă clasa Java conținând implementarea serviciului web
În cazul în care clasa Java ce conține implementarea serviciului web face parte dintr-un pachet, el trebuie precizat complet la denumirea acesteia.
Exemplu:
wsgen -cp . ro.pub.cs.aipi.lab07.businesslogic.BookManager -wsdl -servicename {http://localhost:8080/bookstore/}BookManager
wsimport [options]
unde
* ''options''
^ opțiune ^ semnificație ^
| ''-b '' | indică fișierele de asociere jaxws/jaxb sau scheme suplimentare (fiecare cale trebuie să fie prefixată de opțiunea ''-b'') |
| ''-B'' | transmite opțiunea către compilatorul de scheme JAXB |
| ''-catalog '' | precizează fișierul ce conține catalogul pentru rezolvarea referințelor către entități externe (sunt suportate formatele ''TR9401'', ''XCatalog'', ''Oasis XML'') |
| ''-classpath '' | precizează locația la care pot fi găsite clasele de intrare și extensiile |
| ''-cp '' | indică locația la care pot fi găsite clasele de intrare și extensiile |
| ''-d '' | indică locația la care vor fi plasate fișierele de ieșire |
| ''-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:'' | stabilește un proxy HTTP, având formatul ''[user[:password]@]proxyHost:proxyPort'', unde ''proxyPort'' are valoarea implicită 8080 |
| ''-J'' | transmite opțiunea către compilatorul ''javac'' |
| ''-keep'' | determină păstrarea fișierelor generate |
| ''-p '' | 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 '' | specifică locația la care va fi plasat codul sursă generat |
| ''-target '' | 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 '' | specifică valoarea locației de unde va fi preluat fișierul WSDL conținând specificația serviciului web (parametrul ''@WebServiceClient.wsdlLocation'') |
| ''-clientjar '' | 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 '' | indică locația la care vor fi generate fișierele ce conțin implementarea JWS |
| ''-implServiceName '' | precizează denumirea locală a serviciului pentru implementarea JWS |
| ''-implPortName '' | 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 web
Exemplu:
wsimport -s ../src http://localhost:8080/bookstore/BookManager?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:
* clasa ''Operation'' oferă metode pentru transferul parametrilor metodelor accesibile la distanță;
* clasa ''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.
Spre exemplu, în unei cazul unei proprietăți având tipul ''String'' din cadrul unei clase serializabile, au loc următoarele transformări în momentul transmisiei prin infrastructura de comunicației: ''String'' → ''xs:string'' → ''String''.
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''.
În literatura de specialitate, se foloseşte denumirea de port pentru obiectul delegat corespunzător serviciului web la distanţă, ce conţine interfaţa (SEI – Service Endpoint Interface) definită de acesta.
public class BookManagerClient{
@WebServiceRef(wsdlLocation = "META-INF/wsdl/localhost_8080/07-BookStore-JAXWS-Server/BookManager.wsdl")
private static BookManager_Service service;
public static void main(String[] args) {
service = new BookManager_Service();
BookManager port = service.getBookManagerPort();
Utilities.displayResults(port.getBooksList(), BookInformation.class);
System.out.println("Enter stockpile: ");
int stockpile = Integer.parseInt(bufferedReader.readLine());
Utilities.displayResults(port.getBooksListWithStockpile(stockpile), BookInformationDetailed.class);
}
}
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''.
Locația specificată de parametrul ''wsdlLocation'' poate referi și un fișier WSDL existent pe un server web, de exemplu: http://localhost:8080/bookstore/BookManager?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 [[https://docs.oracle.com/javaee/7/api/javax/xml/ws/Service.html#create-java.net.URL-javax.xml.namespace.QName-|create()]] din cadrul clasei [[https://docs.oracle.com/javaee/7/api/javax/xml/ws/Service.html|javax.xml.ws.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/bookstore/BookManager?wsdl");
QName serviceName = new QName("http://businesslogic.lab07.aipi.cs.pub.ro/BookManager", "BookManager");
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 [[https://docs.oracle.com/javaee/7/api/javax/xml/ws/Service.html#getPort-java.lang.Class-|getPort()]] a obiectului de tip ''Service'', care primește ca parametru clasa pe care o reprezintă (la care va fi convertit):
public T getPort(Class serviceEndpointInterface) throws javax.xml.ws.WebServiceException {}
Un exemplu de obținere a unui port pentru care pot fi invocate ulterior serviciile pot fi:
BookManager port = service.getPort(BookManager.class);
===== Studiu de Caz: Dezvoltarea unui serviciu web folosind mediul de dezvoltare NetBeans 8.1 și serverul de aplicații GlassFish 4.1 =====
==== Dezvoltarea serverului ====
Se creează un proiect de tip //Java Web// → //Web Application//.
{{ :laboratoare:laborator07:netbeans_server01.png?nolink&600 }}
Î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.
{{ :laboratoare:laborator07:netbeans_server02.png?nolink&600 }}
Pentru aplicația web se precizează o denumire, precum și locația unde va fi amplasat proiectul corespunzător.
{{ :laboratoare:laborator07:netbeans_server03.png?nolink&600 }}
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 (''/07-BookStore-JAXWS-Server'').
{{ :laboratoare:laborator07:netbeans_server04.png?nolink&600 }}
Pot fi specificate și alte tehnologii cu care să fie integrat serviciul web (Spring Web MVC, Java Server Faces, Struts, Hibernate).
{{ :laboratoare:laborator07:netbeans_server05.png?nolink&600 }}
Proiectul va conține o resursă de tip //Web Service...//, accesibilă din meniul contextual prin selectarea opțiunii //New//.
{{ :laboratoare:laborator07:netbeans_server06.png?nolink }}
Pentru serviciul web trebuie completate atribute precum:
* denumirea;
* proiectul din care face parte;
* localizarea;
* pachetul în care este inclus codul sursă corespunzător implementării;
* modul de creare al serviciului web:
* de la zero;
* pornind de la o componentă de tip Session Bean (pentru care se indică locația);
* posibilitatea de a implementa un comportament de componentă Java Beans de tip sesiune fără stare.
{{ :laboratoare:laborator07:netbeans_server07.png?nolink&600 }}
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:
* optimizarea transferului datelor binare;
* fiabilitatea transmiterii mesajelor;
* securitate.
{{ :laboratoare:laborator07:netbeans_server08.png?nolink&800 }}
Î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).
{{ :laboratoare:laborator07:netbeans_server09.png?nolink }}
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.
Datele de autentificare pot fi preluate din mediul de dezvoltare NetBeans, accesând //Tools// → //Servers// → //Glassfish Server 4.1// (tab-ul //Common//). De regulă, utilizatorul care deţine toate drepturile este ''admin'', iar parola asociată acestuia este vidă.
{{ :laboratoare:laborator07:netbeans_server10.png?nolink&800 }}
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.
În situația în care operațiile furnizate de serviciul web implică transmiterea (ca parametrii sau ca valori întoarse a metodelor) de obiecte, testarea funcționalității nu este relevantă, întrucât implică transferul de adrese (referințe) a căror valabilitate este restrânsă la nivelul serverului de aplicații, fără a putea fi vizualizat conținutul propriu-zis al acestora.
This XML file does not appear to have any style information associated with it. The document tree is shown below.
==== Dezvoltarea clientului ====
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).
{{ :laboratoare:laborator07:netbeans_client01.png?nolink }}
Se indică locația (URL-ul) la care se găsește specificația WSDL a serviciului web ([[http://localhost:8080/07-BookStore-JAXWS-Server/BookManager?wsdl|]]), pe baza căreia vor fi generate clasele care descriu operațiile ce pot fi invocate.
{{ :laboratoare:laborator07:netbeans_client02.png?nolink&600 }}
Pentru a genera clasele corespunzătoare serviciului web, din meniul contextual asociat referinței la serviciul web, se accesează opțiunea //Refresh//. O astfel de operație este necesară atunci când se modifică funcționalitatea serviciului web (semnăturile metodelor accesibile la distanță, NU și implementarea lor, care este transparentă pentru client).
{{ :laboratoare:laborator07:netbeans_client03.png?nolink }}
De asemenea, este necesar să se bifeze opțiunea //Also replace local wsdl file with original wsdl located at://, astfel încât să se descarce definiția serviciului web (fișierul WSDL) de la locația în care a fost publicat de către server:
{{ :laboratoare:laborator07:netbeans_client04.png?nolink }}
Clasele generate sunt vizibile în secțiunea //Generated Sources (jax-ws)//.
{{ :laboratoare:laborator07:netbeans_client05.png?nolink }}
Opțiunea //Clean// din meniul contextual asociat proiectului determină pierderea claselor generate.
Operaţiile pot fi testate din consola serverului de aplicaţii GlassFish, disponibile la [[http://localhost:8080/07-BookStore-JAXWS-Server/BookManager?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).
{{ :laboratoare:laborator07:netbeans_client06.png?nolink }}
Pentru exemplul considerat, în cazul metodelor ''getBooksList()'', ''getBooksListWithStockpile()'' pot fi vizualizate referinţe (adrese) către obiecte de tip ''BookInformation'' / ''BookInformationDetailed''. În situația în care metodele ar fi primit ca parametri tipuri de date derivate din ''java.lang.Object'', o astfel de consolă nu ar putea fi utilizată, întrucât ar trebui furnizate referințele către aceste obiecte.
Pentru operaţia ''getBooksList()'', formatul mesajelor SOAP interschimbate între client şi server este următorul:
* SOAP Request
* SOAP Response 1My Best Series BooksRussia31957XoXo Publishing a Ninni Group IncConsuming Ocean IslandDora APEL
Pentru operaţia ''getBooksListWithStockpile()'', primind ca parametru un stoc egal 500, formatul mesajelor SOAP interschimbate între client şi server este următorul:
* SOAP Request 500
* SOAP Response 1Basketball50 × 75, EPUB, PluckerAfar, Afrikaans, VietnameseStories of People and Phosphate from BanabaConsuming Ocean IslandDora APEL
===== Activitate de Laborator =====
**0**. Să se cloneze în directorul de pe discul local conținutul depozitului la distanță de la [[https://www.github.com/aipi2015/Laborator07]]. În urma acestei operații, directorul Laborator07 va trebui să conțină subdirectorul ''labtasks'', fișierele ''README.md'' și ''LICENSE''.
student@aipi2015:~$ git clone https://www.github.com/aipi2015/Laborator07.git
**1**. Să se ruleze, folosind MySQL Workbench (sau alt utilitar similar), scripturile ''Laborator07c.sql'' și ''Laborator07l.sql'', localizate în directorul ''scripts'' din cadrul proiectului ''07-BookStore-JAXWS-Server''. Acesta instalează baza de date ''bookstore''.
În cazul în care baza de date ''bookstore'' este deja instalată, nu mai este necesar să se ruleze acest script.Această operație poate dura o perioadă de timp îndelungată, în funcție de configurațiile mașinilor pe care se lucrează.
**2**. Să se modifice credențialele (nume de utilizator și parolă), astfel încât să se poată accesa informațiile din baza de date MySQL.
==== Eclipse ====
Este suficient să se modifice proprietățile ''javax.persistence.jdbc.user'', respectiv ''javax.persistence.jdbc.password'' din fișierul ''persistence.xml'', localizat în directorul ''src/META-INF'', întrucât în această situație este folosit un server local, iar accesul la sistemul de gestiune pentru baze de date se realizează din contextul acestuia.
==== NetBeans ====
În cazul în care accesul la sistemul de gestiune pentru baze de date se realizează din contextul unui server de aplicații (GlassFish 4.1), este necesar să se definească o resursă la nivelul acestuia, referința către aceasta fiind identificată prin intermediul JNDI (Java Naming and Directory Interface).
În acest sens, în fișierul ''persistence.xml'' vor fi realizate următoarele modificări:
* atributul ''transaction-type'' din cadrul elementului ''persistence-unit'' va avea valoarea ''JTA'';
* informațiile cu privire la conexiunea la sistemul de gestiune pentru baze de date vor fi specificate printr-o resursă JNDI, a cărui referință este indicată în cadrul elementului ''jta-data-source'';
* nu mai sunt necesare proprietățile ''javax.persistence.jdbc.url'', ''javax.persistence.jdbc.user'', ''javax.persistence.jdbc.password'', ''javax.persistence.jdbc.driver'', acestea fiind conținute în resursa definită la nivelul serverului de aplicație;
* în secțiunea de proprietăți, se va defini proprietatea ''hibernate.transaction.jta.platform'' cu valoarea ''org.hibernate.service.jta.platform.internal.SunOneJtaPlatform''.
Definirea unei resurse JDBC în cadrul serverului de aplicație GlassFish implică [[https://dev.mysql.com/doc/connector-j/en/connector-j-usagenotes-glassfish-config.html|următoarele etape]]:
**a**. se accesează secțiunea //JDBC Connection Pools// disponibilă în //Resources// → //JDBC// și se apasă butonul //New// pentru a se defini un nou set de conexiuni:
{{ :laboratoare:laborator07:netbeans_glassfish01.png?nolink&800 }}
**b**. se specifică proprietățile:
* //Pool Name//, va avea o valoare definită de utilizator, spre exemplu: ''MySQLConnectionPool'';
* //Resource Type//, trebuie să aibă valoarea ''javax.sql.DataSource'' selectată din lista de opțiuni;
* //Database Driver Vendor//, trebuie să aibă valoarea ''MySQL'' selectată din lista de opțiuni;
Este necesar ca driver-ul de conexiune la sistemul de gestiune pentru baze de date (Connector/J = mysql-connector) să se găsească în classpath-ul serverului de aplicații Glassfish: ''/glassfish/domains//lib''. Implicit, se va folosi ''domain1'' ca denumire a domeniului.
{{ :laboratoare:laborator07:netbeans_glassfish02.png?nolink&800 }}
Se apasă butonul //Next//.
{{ :laboratoare:laborator07:netbeans_glassfish03.png?nolink&800 }}
În secțiunea //Aditional Properties//, trebuie specificate următoarele proprietăți:
* //ServerName// cu valoarea ''localhost'';
* //Port// cu valoarea ''3306'';
* //User// cu valoarea numelui de utilizator prin intermediul căruia se va realiza conexiunea (''root'');
* //Password// cu valoarea parolei prin intermediul căreia se va realiza conexiunea (''StudentAipi2015'');
* //DatabaseName// cu valoarea bazei de date care va fi exploatată (''bookstore'').
{{ :laboratoare:laborator07:netbeans_glassfish04.png?nolink&800 }}
Se apasă butonul //Finish//.
Noul set de conexiuni va fi vizibil în secțiunea //Resources// → //JDBC// → //JDBC Connection Pools//.
{{ :laboratoare:laborator07:netbeans_glassfish05.png?nolink&800 }}
Se accesează resursa creată în panoul //Advanced//.
{{ :laboratoare:laborator07:netbeans_glassfish06.png?nolink&800 }}
În secțiunea //Connection Validation// se specifică următoarele proprietăți, necesare datorită faptului că driver-ul de conexiune la sistemul de gestiune pentru baze de date Connector/J nu suportă optimizarea interogărilor pentru validare:
* //Connection Validation//: ''Required'';
* //Validation Method//: se selectează valoarea ''table'' din lista de opțiuni;
* //Table Name//: se introduce valoarea ''DUAL''.
{{ :laboratoare:laborator07:netbeans_glassfish07.png?nolink&800 }}
Se apasă butonul //Save// astfel încât modificările realizate să fie preluate.
Pentru a se verifica starea conectivității la sistemul de gestiune pentru baze de date, în panoul //General//, se apasă butonul //Ping//. În situația în care rezultatul furnizat este //Ping Succeeded//, conexiunea a fost configurată corect.
{{ :laboratoare:laborator07:netbeans_glassfish08.png?nolink }}
**c**. se accesează secțiunea //JDBC Resources// disponibilă în //Resources// → //JDBC// și se apasă butonul //New// pentru a se defini o nouă resursă:
{{ :laboratoare:laborator07:netbeans_glassfish09.png?nolink&800 }}
**d**. se specifică proprietățile:
* //JNDI Name//, va avea o valoare definită de utilizator, spre exemplu: ''jdbc/MySQLDataSource''; aceeași valoare trebuie să fie specificată și în elementul ''jta-data-source'' din fișierul ''persistence.xml'';
* //Pool Name//, va referi setul de conexiuni definit anterior (spre exemplu, ''MySQLConnectionPool''), disponibil într-o listă de opțiuni.
{{ :laboratoare:laborator07:netbeans_glassfish10.png?nolink&800 }}
Se apasă butonul //Ok//.
Noua resursă va fi vizibilă în secțiunea //Resources// → //JDBC// → //JDBC Resources//.
{{ :laboratoare:laborator07:netbeans_glassfish11.png?nolink&800 }}
===== =====
**3**. Să se ruleze proiectul corespunzător serverului (''07-BookStore-JAXWS-Server'').
==== Eclipse ====
Proiectul se rulează ca aplicație Java (//Run As...// → //Java Application//). Nu este necesară definirea unei configurații de rulare dedicate, întrucât nu trebuie specificați nici argumente în linia de comandă și nici parametrii ai mașinii virtuale. Aplicația va rămâne în execuție întrucât metoda ''Endpoint.publish()'' creează o instanță a unui server local, prin intermediul căruia vor fi expuse metodele corespunzătoare serviciului web.
==== NetBeans ====
Proiectul este instalat la nivelul serverului de aplicații GlassFish 4.1 prin intermediul opțiunii //Deploy// disponibilă la nivelul meniului contextual asociat. Aceasta lansează în execuție serverul de aplicații (dacă acesta nu rulează deja) și copiază în contextul său resursele necesare astfel încât funcționalitățile corespunzătoare serviciului web să fie disponibile la distanță. O astfel de operație poate dura o perioadă de timp semnificativă.
===== =====
**4**. Să se ruleze proiectul corespunzător unuia dintre clienți (''07-BookStore-JAXWS-Client-BookManager''). Să se testeze funcționalitățile expuse prin intermediul serviciului web.
Anterior, trebuie să se genereze clasele care conțin definiția serviciului web (clasa ''_Service''), definițiile metodelor la distanță (clasele ''Operation'', respectiv ''OperationResponse'', pentru fiecare funcționalitate în parte), definițiile entităților transmise ca parametrii sau valori întoarse ale acestora precum și fabrica de obiecte (clasa ''ObjectFactory'').
==== Eclipse ====
Este necesar să se ruleze script-ul ''client[.bat|.sh]'' care rulează comanda ''wsimport'' necesară generării claselor necesare pentru accesarea metodelor expuse prin intermediul serviciului web:
wsimport -s ../src http://localhost:8080/bookstore/BookManager?wsdl
Se indică locația la care este publicat fișierul WSDL conținând definiția serviciului web precum și locația la care va fi plasat codul sursă, prin intermediul opțiunii ''-s''.
==== NetBeans ====
Este necesar să se creeze un client pentru serviciul web (definirea unei resurse de tip //Web Service Client// din secțiunea //New// a meniului contextual asociat proiectului). Aceasta va specifica adresa Internet la care a fost publicat fișierul WSDL ([[http://localhost:8080/07-BookStore-JAXWS-Server/BookManager?wsdl]]). Ulterior, generarea codului sursă se face prin accesarea opțiunii //Refresh// din meniul contextual asociat referinței la serviciul web. Se va folosi opțiunea //Also replace local wsdl file with original wsdl located at//. Clasele generate vor fi evidențiate prin plasarea lor în secțiunea //Generated Sources (jax-ws)//.
===== =====
După ce au fost generate clasele necesare, astfel încât proiectele să nu mai aibă erori, rularea proiectului se face obișnuit (//Run As...// → //Java Application//, pentru Eclipse, respectiv //Run// folosind configurația de rulare implicită, pentru NetBeans).
**5**. Să se expună metodele corespunzătoare clasei ''BookPresentationManager'' din pachetul ''ro.pub.cs.aipi.lab07.businesslogic'', astfel încât acestea să fie accesibile la distanță prin intermediul unui serviciu web același denumire. Să se relanseze în execuție serverul, astfel încât acesta să expună și funcționalitățile serviciului web ''BookPresentationManager''.
a) Se adnotează clasa ''BookPresentationManager'' precum și metodele care vor fi expuse la distanță (cu parametrii lor):
- clasa ''BookPresentationManager'' se adnotează folosind însemnarea ''@WebService(serviceName = "BookPresentationManager")'';
- metodele care vor fi expuse la distanță se adnotează folosind însemnarea ''@WebMethod(operationName = "...")'':
@WebMethod(operationName = "updateBookPresentationPriceForBooksWithMultipleFormats")
@WebMethod(operationName = "makeSupplyOrderBasedOnStockpile")
- parametrii metodelor care vor fi expuse la distanță se adnotează folosind însemnarea ''@WebParam(name = "...")'':
public List updateBookPresentationPriceForBooksWithMultipleFormats(@WebParam(name = "numberOfFormats") int numberOfFormats, @WebParam(name = "amount") double amount);
public List makeSupplyOrderBasedOnStockpile(@WebParam(name = "stockpile") int stockpile);
b) Se adnotează clasele ''BookPresentationInformation'' și ''SupplyOrderInformation'', ale căror instanțe vor fi transmise prin rețea, sub formă de documente XML, astfel încât JAXB să cunoască mecanismul prin care obiectele Java pot fi transformate în scheme XML și invers:
- clasele ''BookPresentationInformation'' și ''SupplyOrderInformation'' vor fi adnotate folosind însemnările ''@XmlRootElement'' și ''@XmlType(name="...")'':
@XmlRootElement
@XmlType(name="BookPresentationInformation")
public class BookPresentationInformation extends PersistentEntity {
// ...
}
- toate metodele de tip getter (cu excepția celei care furnizează identificatorul, ''getId()'') vor fi adnotate folosind însemnarea ''@XmlElement''.
**Eclipse**
c) Este necesar să se publice serviciul web ''BookPresentationManager'' printr-un apel al metodei ''Endpoint.publish()''. Pentru fiecare serviciu web publicat, trebuie să se folosească un port dedicat.
Endpoint.publish("http://localhost:8081/bookstore/BookPresentationManager", new BookPresentationManager());
System.out.println("[BookPresentationManager] successfully registered!");
**NetBeans**
c) Nu este necesar să se realizeze nici o acțiune suplimentară, întrucât clasele adnotate cu însemnarea ''@WebService'' sunt detectate automat ca servicii web (vizibile în secțiunea //Web Services//) al căror conținut este instalat în contextul serverului de aplicații în momentul în care se accesează opțiunea //Deploy//.
**6**. Să se construiască un client care să acceseze funcționalitățile corespunzătoare serviciului web ''BookPresentationManager''. Să se testeze metodele expuse la distanță prin intermediul serviciului web.
a) Se creează un proiect Java.
b) Se generează definițiile de clase necesare pentru accesarea funcționalităților expuse de serviciul web.
**Eclipse**
În directorul ''scripts'' aferent proiectului, se creează un script ''client[.bat|.sh]'' care rulează comanda ''wsimport'' necesară generării claselor necesare pentru accesarea metodelor expuse prin intermediul serviciului web:
wsimport -s ../src http://localhost:8081/bookstore/BookPresentationManager?wsdl
Se indică locația la care este publicat fișierul WSDL conținând definiția serviciului web precum și locația la care va fi plasat codul sursă, prin intermediul opțiunii ''-s''.
**NetBeans**
Este necesar să se creeze un client pentru serviciul web (definirea unei resurse de tip //Web Service Client// din secțiunea //New// a meniului contextual asociat proiectului). Aceasta va specifica adresa Internet la care a fost publicat fișierul WSDL ([[http://localhost:8081/07-BookStore-JAXWS-Server/BookPresentationManager?wsdl]]). Ulterior, generarea codului sursă se face prin accesarea opțiunii //Refresh// din meniul contextual asociat referinței la serviciul web. Se va folosi opțiunea //Also replace local wsdl file with original wsdl located at//. Clasele generate vor fi evidențiate prin plasarea lor în secțiunea //Generated Sources (jax-ws)//.
c) Se definește clasa ''BookPresentationManagerClient'' în pachetul ''ro.pub.cs.aipi.lab07.main'', conținând metoda ''main()'':
- se creează obiectele de tip ''Service'', respectiv ''port'' corespunzătoare serviciului web:
BookPresentationManager_Service service = new BookPresentationManager_Service();
BookPresentationManager port = service.getBookPresentationManagerPort();
- se accesează metodele la distanță, expuse prin intermediul serviciului web:
System.out.println("Enter number of formats: ");
int numberOfFormats = Integer.parseInt(bufferedReader.readLine());
System.out.println("Enter percent amount: ");
double amount = Double.parseDouble(bufferedReader.readLine());
port.updateBookPresentationPriceForBooksWithMultipleFormats(numberOfFormats, amount);
System.out.println("Enter stockpile: ");
int stockpile = Integer.parseInt(bufferedReader.readLine());
port.makeSupplyOrderBasedOnStockpile(stockpile)
d) Pentru afișarea rezultatelor, poate fi util să se definească metoda ''displayResults'' care primește ca parametri o listă de obiecte (derivate din ''PersistentEntity'') precum și clasa care conține tipul propriu-zis, afișând conținutul tuturor câmpurilor, prin intermediul metodelor puse la dispoziție de pachetul [[https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/package-summary.html|java.lang.Reflect]].
**7**. (opțional) Să se expună metodele corespunzătoare clasei ''WriterManager'' din pachetul ''ro.pub.cs.aipi.lab07.businesslogic'', astfel încât acestea să fie accesibile la distanță prin intermediul unui serviciu web având aceeași denumire. Să se relanseze în execuție serverul, astfel încât acesta să expună și funcționalitățile serviciului web ''WriterManager''.
**8**. (opțional) Să se construiască un client care să acceseze funcționalitățile corespunzătoare serviciului web ''WriterManager''. Să se testeze acest metodele expuse la distanță prin intermediul serviciului web.
===== Resurse ======
[[https://docs.oracle.com/javaee/7/tutorial/doc/javaeetutorial7.pdf|Eric Jendrock, Ricardo Cervera-
Navarro, Ian Evans, Kim Haase, William Markito - The Java EE 7 Tutorial]] - capitolele 27, 28\\
[[https://netbeans.org/kb/docs/websvc/jax-ws.html|Getting Started with JAX-WS Web Services]]\\
[[http://www.oracle.com/technetwork/articles/javase/index-140168.html|Java Architecture for XML Binding (JAXB)]]\\
[[http://www.vogella.com/tutorials/JAXB/article.html|JAXB - Tutorial]]\\
[[https://dev.mysql.com/doc/connector-j/en/connector-j-usagenotes-glassfish-config.html|Using Connector/J with GlassFish]]\\
[[https://www-01.ibm.com/support/knowledgecenter/SSAW57_8.5.5/com.ibm.websphere.nd.doc/ae/twbs_devjaxwsendpt.html|Developing JAX-WS Web Services with Annotations]]
===== Soluții =====
[[https://github.com/aipi2015/Laborator07/tree/master/solutions/eclipse|Proiect Eclipse]]\\
[[https://github.com/aipi2015/Laborator07/tree/master/solutions/netbeans|Proiect NetBeans]]