WebStore JAX-WS SOAP WebServices, Stateful Session Bean Óbudai Egyetem, Java Enterprise Edition Műszaki Informatika szak Labor 9 Bedők Dávid 2016.01.25. v0.5
SOAP WebServices 1998, 2000 (v1.1), 2003 (v1.2 W3C ajánlással) Simple Object Access Protocol (SOAP), de 1.2 után nem használjuk kibontva XML SOAP Request message-re XML SOAP Response message érkezik (XSD írja le a type információkat!) Web Services Description Language (WSDL) írja le a kommunikáció lehetőségeit és szabályait Általában HTTP-n keresztül küldjük (hasonlóan a restful webservice-ekhez), ám ez csupán egy gyakori opció Rendkívül jól bővíthető, nagyszerű és kiforrott (és jelenleg is bővülő) szabványok vezérlik Titkosítás és digitális aláírás(ok) támogatása - Web Services Security (WS-Security, WSS) Universal Description Discovery and Integration (UDDI) 2
Top-down vs Bottom-up WS Top-down megközelítés: WSDL-t készítünk, majd ebből gépi úton legeneráljuk a szükséges Java osztályokat (stubokat az XSD alapján, és service a WSDL többi része alapján) Bottom-up megközelítés: Java kódot készítünk (stubok, service, számos annotáció), és ebből generáltatjuk le a WSDL-t Kliens készítéshez csak és kizárólag a WSDL-re van szükség (ettől lesz a történet programozási nyelvek között szabadon átjárható, mégis közel olyan típusos (és egyértelmű, gép és ember számára is), mint pl. a Java) 3
Konfiguráció Encoding Style (WSDL-ben is látszik) Document/Message-Oriented: szabadon formázható XML tartalom RPC: kötötebb (sokszor bővebb), ám gépi úton könnyebben automatizálható Encoding models (WSDL-ben is látszik) Literal: a tartalom illeszkednie kell a WSDL-ben leírt, user által definiált XSD type információkra (XSD validáció). További előny hogy XSLT segítségével az üzenet válasza könnyen és egyértelműen átalakítható mássá (pl. XHTML dokumentummá egy weboldal/webalkalmazás számára) Encoded: csak és kizárólag előre definiált XSD típusok használhatóak (nehézkesebb validáció) Parameter Style (a hivatkozások lesznek máshogy szervezve a WSDL-ben) BARE: nem csomagol semmit a SOAP üzenetekbe, ha olvasható eredményt szeretnénk, mi magunknak kell csomagoló osztályokat készítenünk (mind paraméterként, mind visszatéréti értékként) WRAPPED: a kérés és a válasz paramétereit becsomagolja a művelet köré (átláthatóbb, ám lényegesen hosszabb SOAP üzeneteket eredményezhet) A példákban mi az aláhúzott konfigurációt fogjuk használni, de érdemes kipróbálni más konfigurációt is! 4
WSDL felépítése A megvalósítandó feladat példája alapján <wsdl:definitions xmlns:xsd="http://www.w3.org/2001/xmlschema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns: tns="http://www.qwaevisz.hu/webstore" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:ns2="http://schemas. xmlsoap.org/soap/http" xmlns:ns1="http://www.qwaevisz.hu/fault" name="webstoreservice" targetnamespace="http://www. qwaevisz.hu/webstore"> <wsdl:types> <xs:schema xmlns:xs="http://www.w3.org/2001/xmlschema" xmlns:tns="http://www.qwaevisz.hu/webstore" elementformdefault="unqualified" targetnamespace="http://www.qwaevisz.hu/webstore" version="1.0">...</xs:schema> </wsdl:types> <wsdl:message name="...">...</wsdl:message>... <wsdl:porttype name="webstore"> <wsdl:operation name="..."> <wsdl:input message="..." name="..."></wsdl:input> <wsdl:output message="..." name="..."></wsdl:output> <wsdl:fault message="..." name="..."></wsdl:fault> </wsdl:operation> </wsdl:porttype> <wsdl:binding name="webstoreservicesoapbinding" type="tns:webstore"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> <wsdl:operation name="..."> <soap:operation soapaction="..." style="document"/> <wsdl:input name="..."><soap:body use="literal"/></wsdl:input> <wsdl:output name="..."><soap:body use="literal"/></wsdl:output> <wsdl:fault name="..."><soap:fault name="..." use="literal"/></wsdl:fault> </wsdl:operation> </wsdl:binding> <wsdl:service name="webstoreservice"> <wsdl:port binding="tns:webstoreservicesoapbinding" name="webstoreport"> <soap:address location="http://localhost:8080/webstore/webstoreservice"/> </wsdl:port> </wsdl:service> </wsdl:definitions> WSDL A WSDL egyes részei egymás elemeire vagy egészére hivatkoznak. A service hivatkozik a binding-ra, a binding a porttype-ra és az azon belül definiált operation-ökre, a porttype operation-jei hivatkoznak a message-ekre, a message-ek pedig a WSDL-ben megtalálható XSD schema elemekre (types rész). 5
Feladat Készítsünk egy webáruház webkosarát kezelő SOAP webszolgáltatást! A webkosár az adott user-session idejére tartalmazzon termékeket (márka, megnevezés, egységár). Ha ugyanabból a termékből többet vásárol a felhasználó, csak a darabszámot növeljük a kosárban! Minden kosárnak legyen egy egyedi azonosítója ( session-id ). 6
Kiegészítés Bejelentkezéssel és ehhez kötött session kezeléssel nem foglalkozunk. Ennek megfelelően a kosár egyedi azonosítóját a user állíthatja be, pontosan egyszer (ha többször megpróbálja, adjunk SOAP Fault hibaüzenetet). A webkosár állapotát Stateful Session Bean segítségével fogjuk implementálni. SOAP kliensként a SOAP-UI programot fogjuk megismerni. 7
SmartBear: SoapUI http://www.soapui.org/ version: v5.2.1 install: next-next-finish free, open-source function testing solution SOAP webservices RESTful webservices security support (encryption, digital signatures, etc.) JMS support (HermesJMS integration) http://www.hermesjms.com/ 8
Project felépítése 9
Stateful Session Bean Az implementációt tartalmazó osztály rendelkezik a @Stateful annotációval. Az osztály tartalmazhat instance field-eket, melyek állapota session-önként értelmezett. Ha ugyanaz a Session ismét meghívja a SFSB egy másik metódusát, az EJB container azt a példányt adja vissza kiszolgálásra, amit korábban adott ugyanezen Session számára. @PostConstruct annotációval ellátott metódus akkor fog lefutni, amikor új session hív meg üzleti metódust a bean-en belül. @Remove annotációval ellátott metódus akkor fog lefutni, amikor a session már invalid, nem elérhető 10
Session Bean állapotok és lifecycle Stateless és Singleton Session Bean-ek, illetve Message- Driven Bean-ek esetén Does not exist állapot Ready állapot (üzleti metódus hívásra kész) Stateful Session Bean-ek esetén: Does not exist állapot Ready állapot (aktív, üzleti metódus hívásra kész) Passive állapot Memóriából a secondary storage -re írja a container, mert még szükség lehet rá, de most épp nem használja a user @PrePassivate és @PostActivate metódusok készíthetőek @PostConstruct és @PreDestroy metódus mindegyikhez készíthető 11
Stateful Session Bean WebKosár @Local public interface WebBasketService { WebBasketService.java void setidentifier(string identifier) throws ServiceException; String getidentifier() throws ServiceException; int getbasketsize() throws ServiceException; void additem(product product) throws ServiceException; Basket getcontent() throws ServiceException; A metódusok ugyanahhoz a kommunikációhoz tartoznak, egymással összefüggnek. 12
Egyedi azonosító beállítása SOAP Request <soapenv:envelope xmlns:soapenv="http://schemas.xmlsoap. org/soap/envelope/" xmlns:web="http://www.qwaevisz.hu/webstore"> <soapenv:header/> <soapenv:body> <web:setidentifierrequest> <identifier>testid</identifier> </web:setidentifierrequest> </soapenv:body> </soapenv:envelope> <soap:envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:body> <ns2:setidentifierrequestresponse xmlns:ns2="http://www.qwaevisz. hu/webstore"/> </soap:body> </soap:envelope> soapenv/soap XML névtér: SOAP szabvány által definiált mezők web/ns2 XML névtér: az alkalmazás által definiált mezők SetIdentifierRequest: a művelet megnevezése identifier: a művelethez tartozó paraméter SOAP Response void válasz a SetIdentifierRequestResponse válaszüzenetben 13
Egyedi azonosító beállítása Kapcsolódó WSDL részek WSDL <wsdl:types> <xs:element name="setidentifierrequest" type="tns:setidentifierrequest"/> <xs:element name="setidentifierrequestresponse" type="tns:setidentifierrequestresponse"/> <xs:complextype name="setidentifierrequest"> <xs:sequence><xs:element minoccurs="0" name="identifier" type="xs:string"/></xs:sequence> </xs:complextype> <xs:complextype name="setidentifierrequestresponse"> <xs:sequence/> </xs:complextype> </wsdl:types> <wsdl:message name="setidentifierrequestresponse"> <wsdl:part element="tns:setidentifierrequestresponse" name="parameters"></wsdl:part> </wsdl:message> <wsdl:message name="setidentifierrequest"> <wsdl:part element="tns:setidentifierrequest" name="parameters"></wsdl:part> </wsdl:message> <wsdl:porttype name="webstore"> <wsdl:operation name="setidentifierrequest"> <wsdl:input message="tns:setidentifierrequest" name="setidentifierrequest"></wsdl:input> <wsdl:output message="tns:setidentifierrequestresponse" name="setidentifierrequestresponse"></wsdl:output> <wsdl:fault message="tns:webstoreserviceexception" name="webstoreserviceexception"></wsdl:fault> </wsdl:operation> </wsdl:porttype> <wsdl:binding name="webstoreservicesoapbinding" type="tns:webstore"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> <wsdl:operation name="setidentifierrequest"> <soap:operation soapaction="http://www.qwaevisz.hu/webstore/setidentifier" style="document"/> <wsdl:input name="setidentifierrequest"><soap:body use="literal"/></wsdl:input> <wsdl:output name="setidentifierrequestresponse"><soap:body use="literal"/></wsdl:output> <wsdl:fault name="webstoreserviceexception"><soap:fault name="webstoreserviceexception" use="literal"/></wsdl: fault> </wsdl:operation> </wsdl:binding> 14
WebService Set identifier WSDL location: http://localhost: 8080/webstore/WebStoreService?wsdl WebStoreService.java @WebService(name = "WebStore", servicename = "WebStoreService", targetnamespace = "http://www.qwaevisz.hu/webstore") @SOAPBinding(style = Style.DOCUMENT, use = Use.LITERAL, parameterstyle konfiguráció! = ParameterStyle.WRAPPED) public class WebStoreService { @EJB private WebBasketService service; @WebMethod(action = "http://www.qwaevisz. hu/webstore/setidentifier", operationname = "SetIdentifierRequest") @WebResult(name = "SetIdentifierResponse", partname = "setidentifierpart") public void setidentifier(@webparam(name = "identifier") final String identifier) throws WebStoreServiceException { try { this.service.setidentifier(identifier); catch (final ServiceException e) { throw new WebStoreServiceException(e.getMessage(), e. geterror()); A @WebResult annotáció a visszatérési érték wrappere lesz. A válasz egésze egy SetIdentifierRequestResponse elemen belül lesz megtalálható. 15
Hibakezelés Ha szerver oldalon hiba történik, SOAP Fault üzenetet illendő válaszként adni. A SOAP Fault jelzi a problémát (faultcode és faultstring), és opcionálisan részleteket is közölhet (detail). Utóbbi egy Java kivétel osztály XML serializálásának eredménye lehet! SOAP Fault Response <soap:envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:body> <soap: Fault> < faultcode>soap:server</ faultcode> < faultstring>basket already has an identifier (TESTID).</ faultstring> <detail> <ns3: WebStoreServiceFault xmlns:ns3="http://www.qwaevisz.hu/fault" xmlns:ns2="http://www.qwaevisz.hu/webstore"> < code>42</code> < message>basket already has an identifier (TESTID).</ message> </ns3: WebStoreServiceFault> </ detail> </soap:fault> </soap:body> </soap:envelope> 16
Custom SOAP Fault @WebFault(name = "WebStoreServiceFault", targetnamespace = "http: //www.qwaevisz.hu/fault") public class WebStoreServiceException extends Exception { private final ServiceError faultinfo; A kivétel dobása a @WebMethod-ból fogja indikálni a SOAP Fault elkészítését, amennyiben a kivétel @WebFault annotációval rendelkezik. WebStoreServiceException.java public WebStoreServiceException(String message, ServiceError faultinfo) { super(message); this.faultinfo = faultinfo; public WebStoreServiceException(String message, ServiceError faultinfo, Throwable cause) { super(message, cause); this.faultinfo = faultinfo; public ServiceError getfaultinfo() { return this.faultinfo; A kivétel message attribútuma kerül a SOAP Fault faultstring részébe. A getfaultinfo() getter metódus által visszaadott DTO XML szerializálás során bekerül a SOAP Fault detail részébe. 17
ServiceError DTO Java Architecture for XML Binding (JAX-B) @XmlRootElement(name = "errorstub") public class ServiceError implements Serializable { private final int code; private final String message; public ServiceError() { this(0, null); public ServiceError(int code, String message) { this.code = code; this.message = message; @XmlElement(name = "code") public int getcode() { return this.code; @XmlElement(name = "message") public String getmessage() { return this.message; ServiceError.java JAX-B: Alapértelmezés szerint az XML szerializálás @XmlAccessorType(XmlAccessType.PROPERTY) konfiguráció szerint történik, vagyis a getter metódusok határozzák meg a kiírandó mezőket. 18
Egyedi azonosító lekérése SOAP Request <soapenv:envelope xmlns:soapenv="http://schemas.xmlsoap. org/soap/envelope/" xmlns:web="http://www.qwaevisz.hu/webstore"> <soapenv:header/> <soapenv:body> <web:getidentifierrequest/> Paraméter nélküli kérdés. </soapenv:body> </soapenv:envelope> SOAP Response <soap:envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:body> <ns2:getidentifierrequestresponse xmlns:ns2="http://www.qwaevisz. hu/webstore"> <GetIdentifierResponse>TESTID</GetIdentifierResponse> </ns2:getidentifierrequestresponse> </soap:body> </soap:envelope> 19
WebService Egyedi azonosító lekérése WebStoreService.java @WebMethod(action = "http://www.qwaevisz.hu/webstore/getidentifier", operationname = "GetIdentifierRequest") @WebResult(name = "GetIdentifierResponse", partname = "getidentifierpart") public String getidentifier() throws WebStoreServiceException { try { return this.service.getidentifier(); catch (final ServiceException e) { throw new WebStoreServiceException(e.getMessage(), e. geterror()); Egyszerű String válasz JAX-B konverzió egyértelmű. 20
Webkosár méretének lekérdezése SOAP Request <soapenv:envelope xmlns:soapenv="http://schemas.xmlsoap. org/soap/envelope/" xmlns:web="http://www.qwaevisz.hu/webstore"> <soapenv:header/> <soapenv:body> <web:getbasketsizerequest/> </soapenv:body> </soapenv:envelope> SOAP Response <soap:envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:body> <ns2:getbasketsizerequestresponse xmlns:ns2="http://www.qwaevisz. hu/webstore"> <GetBasketSizeResponse>0</GetBasketSizeResponse> </ns2:getbasketsizerequestresponse> </soap:body> </soap:envelope> 21
WebService Webkosár méretének lekérdezése WebStoreService.java @WebMethod(action = "http://www.qwaevisz.hu/webstore/getbasketsize", operationname = "GetBasketSizeRequest") @WebResult(name = "GetBasketSizeResponse", partname = "getbasketsizepart") public int getbasketsize() throws WebStoreServiceException { try { return this.service.getbasketsize(); catch (final ServiceException e) { throw new WebStoreServiceException(e.getMessage(), e. geterror()); Egyszerű int válasz JAX-B konverzió egyértelmű. 22
Elem elhelyezése a kosárban SOAP Request <soapenv:envelope xmlns:soapenv="http://schemas.xmlsoap. org/soap/envelope/" xmlns:web="http://www.qwaevisz.hu/webstore"> <soapenv:header/> <soapenv:body> <web:additemrequest> <item brand="philips" name="22pfh4000" price="45900"/> </web:additemrequest> </soapenv:body> </soapenv:envelope> A termék fej elem neve item, melyet a @WebMethod paramétere határoz meg. A kérés paramétere egy terméket azonosít a saját adataival! SOAP Response <soap:envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:body> <ns2:additemrequestresponse xmlns:ns2="http://www.qwaevisz. hu/webstore"/> void válasz </soap:body> </soap:envelope> 23
WebService Elem elhelyezése a kosárban WebStoreService.java @WebMethod(action = "http://www.qwaevisz.hu/webstore/additem", operationname = "AddItemRequest") @WebResult(name = "AddItemResponse", partname = "additempart") public void additem(@webparam(name = "item") final Product product) throws WebStoreServiceException { try { this.service.additem(product); catch (final ServiceException e) { throw new WebStoreServiceException(e.getMessage(), e. geterror()); A Product XML szerializációhoz már kell néhány annotáció! 24
JAX-B annotációk Termék DTO @XmlRootElement(name = "product") @XmlAccessorType(XmlAccessType.FIELD) public class Product { @XmlAttribute(name = "brand") private Brand brand; @XmlAttribute(name = "name") private String name; @XmlAttribute(name = "price") private int price; public Product() { public Product(Brand brand, String name, int price) { this.brand = brand; this.name = name; this.price = price; public Brand getbrand() { return this.brand; public void setbrand(brand brand) { this.brand = brand; public String getname() { return this.name; public void setname(string name) { this.name = name; public int getprice() { return this.price; public void setprice( int price) { this.price = price; Product.java Ha @XmlAccessorType(XmlAccessType. FIELD) akkor a mezőket láthatjuk el annotációval. Ha nem XML elemeket, hanem egy elem attribútumát szeretnénk, akkor az @XmlAttribute annotációt használjuk az @XmlElement helyett. Az Brand Enum-ból automatikusan enumaration készül az XSD-ben (WSDL részeként), illetve a JAX-B ennek megfelelően automatikusan szerializálja. 25
Kosár tartalmának lekérdezése <soapenv:envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http: //www.qwaevisz.hu/webstore"> <soapenv:header/> <soapenv:body> <web:getcontentrequest/> </soapenv:body> </soapenv:envelope> <soap:envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:body> <ns2: GetContentRequestResponse xmlns:ns2="http://www.qwaevisz.hu/webstore" > < GetContentResponse identifier="testid" total="345700"> <items quantity="1" total="45900"> <product brand="philips" name="22pfh4000" price="45900"/> </items> <items quantity="2" total="299800"> <product brand="panasonic" name="tx-42as650e" price="149900"/> </items> </ GetContentResponse> </ns2: GetContentRequestResponse> </soap:body> </soap:envelope> SOAP Request SOAP Response Számolt értékkel is szeretnénk ellátni a kimenetet: total érték a GetContentResponseban illeve az items fej elemekben is (részösszeg)! 26
WebService Kosár tartalmának lekérdezése WebStoreService.java @WebMethod(action = "http://www.qwaevisz.hu/webstore/getcontent", operationname = "GetContentRequest") @WebResult(name = "GetContentResponse", partname = "getcontentpart") public Basket getcontent() throws WebStoreServiceException { try { return this.service.getcontent(); catch (final ServiceException e) { throw new WebStoreServiceException(e.getMessage(), e. geterror()); 27
JAX-B annotációk WebKosár DTO @XmlRootElement(name = "basket") @XmlAccessorType(XmlAccessType.PROPERTY) public class Basket { Basket.java private String identifier; private final List<BasketItem> items; @XmlAttribute(name = "identifier") public String getidentifier() { return this.identifier; @XmlElement(name = "items") public List<BasketItem> getitems() { return this.items; @XmlAttribute(name = "total") public int gettotal() { int sum = 0; for (final BasketItem item : this.items) { sum += item.gettotalprice(); return sum; A gettotal() metódus egy total számolt érték lesz az XML kimenetben. 28
Kommunikáció Összefoglalás 29