20
Web servisi – Java vs .NET Tomislav Jakupić Koprivnica, 07.11.2009

Web servisi – Java vsdocshare01.docshare.tips/files/20370/203702890.pdf · Java platforma podržava razvoj Web servisa kroz JAX-WS (Java API for XML-Web Services), bilo da ... odnosno

  • Upload
    vulien

  • View
    231

  • Download
    5

Embed Size (px)

Citation preview

Web servisi – Java vs .NET Tomislav Jakupić

Koprivnica, 07.11.2009

2

Uvod u Web servise World Wide Web Consortium (http://www.w3.org) definira Web servis (Web service) kao softverski sustav koji podržava funkcionalnu interakciju među računalima preko mreže. Sučelje takvog sustava opisano je u računalu prijateljskom formatu (WSDLu - Web Services Description Language). Ostali sustavi komuniciraju s Web servisom na način propisan spomenutim opisom, koristeći SOAP (Simple Object Access Protocol) preko HTTPa i uz serijalizaciju XMLa. Unatoč danoj definiciji W3Ca koja je dosta konkretna, općenito možemo reći da je izraz 'Web servis' još uvijek dosta neodređen, nedovoljno definiran i njegovo značenje se još uvijek razvija. No kada govorimo o Web servisima u načelu govorimo o klijentima i serverima koji najčešće komuniciraju preko HTTPa (Hypertext Transfer Protocol) odnosno Weba. Možemo reći da su Web servisi zapravo Internet APIji (Application Programming Interfaces) ili distribuirane aplikacije kojima se pristupa preko mreže i koji se izvršavaju na udaljenom računalu na kojem Web servis egzistira, a mogu im pristupiti razni uređaji (npr. računalo, mobitel itd.). Tu možemo spomenuti i druge poznate pristupe koji imaju sličnu funkcionalnost kao npr. OMGova (Object Management Group) Common Object Request Broker Architecture (CORBA), Microsoftov Distributed Component Object Model (DCOM) ili Sunov Java Remote Method Invocation (RMI). Web servisi se mogu podjeliti u grubo u dvije skupine:

• Web servisi bazirani na SOAPu – opisani su definicijom W3Ca • RESTful (REpresentational State Transfer) Web servisi – u zadnje vrijeme

dobivaju na popularnosti. Ovakvi Web servisi su bolje usklađeni s HTTPom i Web preglednicima, budući da koriste standardne POST, PUT, GET i DELETE HTTP metode. Ne zahtjevaju XML (odnosno SOAP) za komunikaciju ni WSDL opise.

Podjela nije u potpunosti jasna, budući da su Web servisi bazirani na SOAPu zapravo specijalni slučaj RESTful Web servisa. Originalno SOAP je značio Simple Object Access Protocol, no verzijom 1.2 značenje akronima je napušteno i sada je samo SOAP. Zbog sličnosti akronima mogli bismo ga zamjeniti sa Service Oriented Architecture (SOA) Protokolom. SOA nije jednostavan pojam i kasnije ćemo ga detaljnije opisati, ali što god to bilo, možemo zaključiti iz samog naziva da su servisi okosnica S(ervice)OA pristupa razvoja aplikacija. Pomoću SOAP baziranih servisa možemo implementirati SOA koncepte, gdje je osnova komunikacije poruka, a ne operacija. Dakle, SOAP i SOA su različite stvari, ali njihova povezanost postoji. U uobičajenom scenariju razmjene poruka (ili tzv. uzorku razmjene poruka (message exchange pattern – MEP) gdje imamo zahtjev (request) i odgovor (response), na nižoj razini apstrakcije, klijentove SOAP biblioteke šalju SOAP poruku kao zahtjev za uslugom (service request), a na strani Web servisa SOAP biblioteke šalju SOAP poruku odgovora (service response). U ovom scenariju, klijentu i Web servisu SOAP infrastruktura je praktički nevidljiva i možemo reći „nebitna“, ali osigurava uspješnu komunikaciju.

3

Za razliku od SOAP baziranih Web servisa koje karakterizira postojanje standarda (održava ga W3C), mnogo skupova alata i softverskih biblioteka, REST bazirani Web servisi nisu standardizirani i nemaju toliko alata niti softverskih bibilioteka. REST se smatra svojevrsnim „protuotrovom“ za rastuću složenost SOAP baziranih Web servisa. Osim vjerojatno za vrijeme testiranja, klijent SOAP ili REST baziranih Web servisa je najčešće aplikacija bez grafičkog sučelja koja može biti napisana u bilo kojem programskom jeziku ili razvijena u bilo kojem razvojnom okruženju. Transparentnost s obzirom na programski jezik je ključ i najvažnija karakteristika servisa. Ako je Web servis napisan u Javi, a koristi ga aplikacija napisana u Perlu, mora postojati posrednik koji će neutralizirati razlike u tipovima podataka između različitih programskih jezika. Tu dolaze na scenu XML tehnologije kao tehnologije za opis podataka i razmjenu dokumenata, a preuzimaju zadaću posrednika. Kod SOAP servisa SOAP poruke (zahtjev i odgovor) su XML dokumenti. Kod REST Web servisa klijent može poslati standardni HTTP zahtjev, a dobiti čisti XML ili SOAP dokument kao odgovor. Karakteristike koje odjeljuju Web servise od ostalih distribuiranih softverskih sustava, poput spomenutih CORBAe, DCOMa ili RMIa su:

• Otvorena infrastruktura – Web servisi su bazirani na HTTP, XML i ostalim protokolima koji su poznati i prihvaćeni

• Transparentnost s obzirom na programski jezik • Modularni dizajn – Web servisi se trebaju razvijati modularno, tako da se na

temelju postojećih servisa mogu generirati novi Iz navedenog lako možemo doći do zaključka zašto uopće trebamo Web servise. Prvi i najočitiji odgovor je interoperabilnost softverskih sustava na različitim računalima i platformama, pisanim u različitim programskim jezicima, te njihova dostupnost standardnim i općepoznatim komunikacijskim protokolima. Web servisi su dakle distribuirani sustavi koji (obično) komuniciraju preko HTTPa, ali mogu komunicirati i preko ostalih poznatih protokola. Sadržaj komunikacije je strukturirani tekst, odnosno XML dokumenti, koji se mogu analizirati, transformirati, spremati ili obrađivati na druge načine. Ako se ukaže potreba kada sustav zahtijeva veću efikasnost, Web servisi mogu isporučiti i podatke u binarnom obliku. Naposlijeku, područje Web servisa je u konstantnom razvoju, a stvarni svijet je mjesto gdje se Web servisi razvijaju upravo sada, pa je važno znati nešto o ovoj temi.

Klijent Web servisa SOAP Web servis

SOAP Biblioteke SOAP Biblioteke

Zahtjev za uslugom

Odgovor

4

Web servisi u Javi Java platforma podržava razvoj Web servisa kroz JAX-WS (Java API for XML-Web Services), bilo da se radi o SOAP ili REST baziranim Web servisima. Također treba spomenuti projekt Jersey dodatno samo za razvoj REST baziranih Web servisa, službeno nazvan JAX-RS (Java API for XML-RESTful Web Services). JAX-WS je dio Metro Web Services Stacka, ili kraće Metro. Osnovni dio Metroa je sadržan u Java 6 Standard Ediciji. Međutim, Metro sadrži puno više nego je uključeno u Java 6 SE, pa se može i zasebno skinuti (https://wsit.dev.java.net). Također, Metro je integriran u Sunovu aplikacijskom serveru pod imenom GlassFish. Uzevši u obzir rečeno, Web servis može biti razvijen kao dio jednog od sljedećih scenarija:

• Samo Java SE – okruženje koja nam omogućava brzo i jednostavno postavljanje Web servisa i klijenata. Preduvjet je Java 6 SE SDK.

• Java u kombinaciji s punom verzijom Metroa – ova platforma nam omogućuje korištenje naprednijih mogućnosti Metroa koji nisu dio Java 6 SE platforme. U kombinaciji s punom verzijom Metroa, možemo koristiti i Java SE verziju 5.

• Tomcat Web kontejner – radi se o samostalnom Java Web kontejneru koji nam omogućuje postavljanje Web servisa slično kao i običnih servleta, JSP ili JSF stranica.

• Aplikacijski poslužitelj kao npr. GlassFish – postavljanjem Web servisa unutar GlassFish aplikacijskog servera servisu omogućujemo interakciju s drugim komponentama u višeslojnoj Java EE arhitekturi (JMS, JNDI, EIS slojem, EJBima itd.).

JAX-WS specifikacija omogućuje da se jednom razvijeni Web servis može postaviti u bilo kojem od navedenih scenarija. Za potrebe izlaganja mi ćemo razviti Web servis samo koristeći Java SE okruženje.

Web servisi u .NETu Od izdavanja .NET Frameworka verzije 3.0 Microsoft je uveo novi način razvoja Web servisa pomoću skupa alata WCF (Windows Communication Foundation). Do tog trenutka nije bio jednostavan zadatak razvijati takve aplikacijske komponente koje će međusobno komunicirati porukama upravo zbog činjenice što je Microsoft nudio više tehnologija za tu namjenu, između ostalog ASP.NET Web Services, Web Service Enhancements 3.0 (WSE), MSMQ, Enterprise Services, .NET Remoting, te System.Messaging namespace. Upravo zbog toga što je programeru na raspolaganju mnogo tehnologija, svaka sa svojim pozitivnim i negativnim stranama, teško odlučiti koju odabrati u određenoj situaciji. U tu svrhu je napravljen WCF. WCF je zapravo cjelovita jedinstvena platforma koja omogućuje izradu ranije spomenutih SOA aplikacija, odnosno servisa kao osnove SOA arhitekture.

5

Service Oriented Architecture SOA (Service Oriented Architecture) je arhitektura neovisna o platformi orjentirana servisima baziranim na principu razmjene poruka. Kao rezultat toga unutar sustava se razmjenjuju poruke, a te iste poruke se mogu koristit unutar drugih sustava koji bi inače međusobni bili nespojivi. Kada gledamo unatrag, možemo uočiti postpuno sazrijevanje prema SOA modelu. Osamdesetih godina prošlog stoljeća dogodila se revolucija u obliku objektno-orjentiranog modela, gdje je sve bilo objekt. S dolaskom objektno-orjentiranog modela, njegovi principi su široko prihvaćeni kao pravi način modeliranja entiteta u domeni razvoja aplikacijskih sustava. Devedesetih godina je napravljen još jedan korak naprijed, a to je tzv. component-oriented model odnosno model orjentiran komponentama. Ovaj model omogućio je učahurivanje usko povezanih objekata unutar neovisnih komponenata. Nedugo zatim je napravljen iskorak prema SOA modelu i to zbog toga što su se developeri i arhitekti sreli sa potrebom da komponente distribuiraju tako da budu dostupne onima kome su potrebne bez obzira na razlike u platformi, arhitekturi itd.. Preduvjet za ovakav sustav je uspješna razmjena poruka između međusobno nekomaptibilnih „strojeva“, a uz to poruke trebaju uključiti nekakve dodatne podatke (metadata) koji opisuju način na koji se poruke mogu koristiti. Jedan od osnovnih principa SOA pristupa jest da su granice prilikom razvoja strogo postavljene. To znači da izvori podataka, programska logika ili entiteti koriste sučelja da bi preko njih podatke ili usluge pružili vanjskom svijetu. Sučelja dakle omogućuju da se sakrije unutarnje funkcioniranje servisa prema vanjskom svijetu, a isto tako omogućuju da se unutarnja logika promjeni bez ikakvog utjecaja na korisnike servisa. Servisi su autonomni entiteti. Pojedinačni servisi se ažuriraju neovisno jedni od drugih. To znači da sustav, koji je sastavljen od više servisa, ne ažurira se kao cjelina, već se pojedini servisi ažuriraju neovisno i ne trebaju čekati jedni na druge. Servisi su bazirani na ugovorima, odnosno svi servisi imaju svoj ugovor koji definira što je potrebno da bi klijenti konzumirali objavljene usluge (obično se realizira kroz WSDL dokumente) i klijenti moraju prihvatiti taj ugovor da bi mogli koristiti servis. Ugovore nadopunjuju sheme (schemes) koje detaljno definiraju podatke koji se šalju kao argumenti servisu ili vraćaju kao rezultati obrade.

6

Osnove XML XML (Extensible Markup Language) je jezik odnosno specifikacija koja daje niz pravila za kodiranje elektroničkih dokumenata. Ciljevi specifikacije su jednostavnost, općenitost i upotrebljivost preko Interneta. Format podataka je tekstualan s jakom podrškom za korištenje Unicode-a, pa tako i za sve jezike diljem svijeta. Iako su primarni fokus XMLa dokumenti, široko je rasprostranjen u prikazu svih oblika podataka, pa tako i kod Web servisa. Osnovne sastavnice XML dokumenta su: oznake (markup) i sadržaj. Oznake uvijek počinju s '<' i završavaju s '>', ili počinju s '&' i završavaju s ';'. Sve ostalo je sadržaj. Oznake možemo podijeliti u:

• oznake (u užem smislu) – tag-ovi (<test>, </test> ili <test />), • elemente (<element>sadržaj</element>), • atribute (<element atribut=“vrijednost“>sadržaj</element>), • deklaracije (<?xml version="1.0" encoding="UTF-8" ?>).

XML dokument mora biti dobro oblikovan (well-formed) za što postoji niz pravila, od kojih nabrajamo samo važnija: (1) dokument sadrži samo ispravno kodirane dozvoljene Unicode znakove, (2) u sadržaju se ne pojavljuju rezervirani znakovi (>, <, ;, &), (3) početni, završni i prazni tagovi moraju biti ispravno ugnježđeni, ne smije nedostajati tag ili se preklapati, (4) nazivi tagova su osjetljivi na velika i mala slova, (5) postoji samo jedan korjenski „root“ element koji sadrži sve ostale. Uz to što mora biti dobro oblikovan, XML dokument može biti i valjan (valid). U tom slučaju mora postojati dodatna datoteka u obliku DTDa (Document Type Definition) ili XML Schema-e u kojem se nalazi točan opis strukture dokumenta u specifičnom formatu. DTD je najstariji jezik za opis strukture XML dokumenta. Njegov nasljednik je XML Schema, odnosno XSD (XML Schema Definition). XSD je mnogo moćniji od DTDa, posebno zato što mu je sustav za opis tipova podataka mnogo širi i bogatiji, omogućuje postavljanje detaljnijih ograničenja na XML dokumente, a osim toga XSD dokumenti su formatirani kao XML dokumenti pa se i lakše obrađuju. XML namespace-i koriste se za jedinstveno imenovanje elemenata i atributa u XML dokumentu. Jedna XML instanca (dokument) može sadržavati imena elemenata ili atributa iz više različitih XML „vokabulara“ (skupova dokumenata). Namespace-i sprečavaju koliziju naziva elemenata i atributa, odnosno omogućuju postojanje istih naziva unutar različitih vokabulara i njihovo paralelno korištenje. Namespace se u dokumentu definira atributom xmlns, odnosno xmlns:ključ, a vrijednost atributa je obično URI (Uniform Resource Identifier). Ključem se onda kasnije naznačuju elementi i atributi koji pripadaju konkretnom namespace-u na način ključ:element ili ključ:atribut. Namjena URIa nije da bi se dohvatio nekakv dokument, što se možda može zaključiti budući da se uglavnom radi o Web adresama, već da jednoznačno identificira namespace na način čitljiv ljudima, ali opet da smanji mogućnost pojave istih identifikatora. U daljnjim primjerima često ćemo se susretati s XML namespace-ima.

7

Implementacija Web servisa u Java tehnologijama Za prikaz principa kod razvoja Web servisa razvit ćemo jednostavan SOAP Web servis koristeći samo Java 6 platformu koja ima ugrađenu podršku za već spomenuti Java API for XML-Web Services, odnosno JAX-WS. Prateći najbolju praksu za razvoj Web servisa implementacija se sastoji od dva dijela:

• sučelje Web servisa - SEI (Service Endpoint Interface) • implementacija Web servisa – SIB (Service Implementation Bean) – Web servis ćemo implementirati u obliku obične Java klase (POJO – Plain Old Java Object)

Sučelje Web servisa – SEI package servisi.primjer1; import javax.jws.WebService; import javax.jws.WebMethod; import javax.jws.soap.SOAPBinding; import javax.jws.soap.SOAPBinding.Style; @WebService @SOAPBinding(style = Style.RPC) public interface VremenskiServer { @WebMethod String getVrijeme(); }

Objašnjenje: anotacija @WebService naznačuje da je Java sučelje SEI (Service Endpoint Interface). Anotacija @WebMethod naznačuje da je metoda sučelja getVrijeme() operacija servisa koju će klijenti moći pozivati. @SOAPBinding anotacija definira način funkcioniranja servisa i postavke u WSDLu, odnosno sporazumu kojega moraju „prihvatiti“ svi klijenti koji žele koristiti servis. Style.RPC koja nam trenutno najbolje odgovara, a o čemu ćemo detaljnije govoriti kasnije. Implementacija Web servisa – SIB package servisi.primjer1; import java.util.Date; import javax.jws.WebService; @WebService(endpointInterface = "servisi.primjer1.VremenskiServer") public class VremenskiServerImpl implements VremenskiServer { public String getVrijeme() { return new Date().toString(); } }

Objašnjenje: anotacija @WebService i njena varijabla endpointInterface u ovom slučaju služe za povezivanje SIB (Service Implementation Bean) sa sučeljem (SEI). Web metoda se ne naznačuje posebno.

8

Nakon što smo implementirali SEI i SIB moramo objaviti Web servis kako bi ga klijenti mogli koristiti. Objavu vršimo na sljedeći način: Endpoint.publish("http://127.0.0.1:9876/vrijeme", new VremenskiServerImpl ());

Statička metoda publish klase javax.xml.ws.Endpoint uzima dva argumenta. Prvi argument je adresa na kojoj ćemo objaviti naš servis i preko koje će klijenti pristupati servisu. Adresa, kao što je vidljivo sadrži IP adresu (ili naziv) servera, port i proizvoljnu putanju /vrijeme. Drugi argument je instanca našeg SIBa. WSDL ovog Web servisa ne moramo sami pisati već se on automatski generira i možemo mu pristupiti preko adrese http://127.0.0.1:9876/vrijeme?wsdl gdje možemo usput i testirati servis. Klijentski kod možemo generirati automatski pomoću alata wsimport koji dolazi kao dio Java 6 SE SDK paketa. Nalazi se obično u direktoriju C:\Program Files\Java\ jdk1.6.0_14\bin. Kao argument programu stavljamo datoteku WSDLa na temelju kojega alat generira sučelje servisa (SEI) i kod potreban za pozivanje udaljenih operacija servisa.

WSDL U poslovnom svijetu ugovor je obvezujući sporazum između dvije ili više strana koji specificira isporuku dobara ili usluga za neku cijenu. U svijetu servisa vrijedi zapravo isto, odnosno to je sporazum između dvije ili više strana koji specificira razmjenu poruka i uvjete koje poruke moraju zadovoljavati. Taj ugovor u svijetu servisa predstavlja WSDL dokument. On daje važne informacije o krajnjim točkama servisa, operacijama servisa i tipovima podataka vezanim za te operacije. Sada ćemo malo pogledati strukturu WSDL na našem primjeru: <?xml version="1.0" encoding="UTF-8" ?> <definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://primjer1.servisi/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://primjer1.servisi/" name="VremenskiServerImplService"> <types /> <message name="getVrijeme" /> <message name="getVrijemeResponse"> <part name="return" type="xsd:string" /> </message> <portType name="VremenskiServer"> <operation name="getVrijeme" parameterOrder=""> <input message="tns:getVrijeme" /> <output message="tns:getVrijemeResponse" /> </operation> </portType>

9

<binding name="VremenskiServerImplPortBinding" type="tns:VremenskiServer"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="rpc" /> <operation name="getVrijeme"> <soap:operation soapAction="" /> <input> <soap:body use="literal" namespace="http://primjer1.servisi/" /> </input> <output> <soap:body use="literal" namespace="http://primjer1.servisi/" /> </output> </operation> </binding> <service name="VremenskiServerImplService"> <port name="VremenskiServerImplPort" binding="tns:VremenskiServerImplPortBinding"> <soap:address location="http://127.0.0.1:9876/vrijeme" /> </port> </service> </definitions>

Korjenski element WSDL dokumenta je definitions element. WSDL daje definicije grupirane u sljedeće sekcije:

• Sekcija types – nije obavezna, sadrži definicije složenih tipova podataka ako postoje i to najčešće u obliku XSDa (XML Schema Definition). Ovaj dio možemo još nazvati i podatkovnim ugovorom (data contract). Ova sekcija dakle sadrži ili ukazuje na vanjski XSD. Ako je sekcija prazna, kao što je u našem primjeru, servis koristi samo jednostavne tipove podataka kao što je i xsd:string.

• Sekcija messages – definira poruke koje se pojavljuju kod servisa. Poruke sadrže tipove podataka definirane u prethodnoj sekciji, ili ako je ona prazna, sadrže standardne tipove podataka. Nadalje, redoslijed poruka (kasnije definiran u sekciji portType) ukazuje na to kakav je uzorak funkcioniranja servisa. Naznačene su, iz gledišta servisa, ulazne (input) i izlazne (output) poruke. Ako je redoslijed poruka in / out, tada se radi o standardnom request/response uzorku, a ako je redoslije obrnut radi se o tzv. solicit/response uzorku u kojem servis inicira komunikaciju, a klijent šalje odgovor.

• Sekcija portType – prezentira servis kao imenovane operacije, a svaka operacija opisana je jednom ili više poruka. Operacije nose ime kao metode označene anotacijom @WebMethod. U našem primjeru u odjeljku portType grupirane su operacije koje Web servis nudi, u našem slučaju to je jedna operacija getVrijeme deklarirana u sučelju Web servisa (SEI), a implementirana u SIBu. Odjeljak portType je zapravo sličan SEIu po tome što daje apstraktni prikaz operacija koje nudi servis, bez implementacije koja se nalazi na drugom mjestu. Za svaku operaciju definirani su elementi input i output s atributom message, a predstavljaju (ranije definirane u sekciji messages) ulazne, odnosno izlazne poruke za konkretnu operaciju servisa. Kod izvršavanja te su poruke SOAP dokumenti. O redoslijedu poruka smo govorili u sekciji messages.

• Sekcija binding – u ovom dijelu se konkretiziraju WSDL definicije. Kao što je portType srodan sučelju servisa, tako je binding srodan implementaciji servisa. Kao što SIB daje konkretne informacije o servisu, tako i sekcija binding ima istu funkciju u WSDLu. Ova je sekcija najkompliciranija jer mora dati implementacijske detalje servisa prethodno apstraktno definiranog u sekciji portType, a to su: (1) transportni protokol koji će se koristiti za slanje i primanje SOAP poruka. U aplikacijskom sloju (OSI model) to može biti HTTP ili SMTP

10

(Simple Mail Transport Protocol). HTTP je daleko najpopularniji. WSDL u našem primjeru sadrži sljedeću liniju koja definira transportni protokol: <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="rpc" />. Sljedeća stvar koju treba definirati je (2) style atribut . On može poprimiti dvije vrijednosti i to: document ili rpc. Ako drugačije ne specificiramo vrijednost parametra je uvijek document, zbog čega smo u kodu eksplicitno definirali kao rpc (sjetimo se linije: @SOAPBinding(style = Style.RPC)). Ako je stil „document“ poruke sadrže cjelovite (XML) dokumente (kao npr. narudžbe), dok „rpc“ implicira da će poruka sadržavati ili parametre ili povratne vrijednosti obrade (kao što je u našem primjeru slučaj). Na kraju moramo definirati (3) format podataka u SOAP porukama (use atribut soap:body elementa). Postoje dvije mogućnosti: literal i encoded. „literal“ implicira da je informacija u poruci doslovno onakva kao što je definirano u XML shemi u types sekciji. „encoded“ implicira da je informacija u poruci dodatno kodirana SOAP pravilima kodiranja ili nekim drugim pravilima kodiranja.

• Sekcija service – specificira jednu ili više krajnjih točaka (endpoint) na kojima su dostupne operacije servisa. Krajnje točke su specificirane u obliku port elemenata, a za svaki port elemenat imamo prateću definiciju bindinga, te preko bindinga i definiciju portType-a (sučelja).

Budući da je WSDL ugovor koji klijent mora prihvatiti da bi mogao koristiti Web servis, on je osnova za izradu klijenta. Iz WSDLa klijent može zaključiti osnovne informacije, uključujući i tipove podatake, koje će mu trebati za pristup i korištenje servisa. Iz našeg WSDLa klijent može zaključiti da imamo operaciju getVrijeme, koja ne očekuje argumente i vraća znakovni niz kao rezultat. Svaki programski jezik nudi alat koji može generirati kod za pristup pojedinom servisu iz WSDL dokumenta. Kod Jave taj alat se zove wsimport. Klijent Web servisa package servisi.primjer1; import javax.xml.namespace.QName; import javax.xml.ws.Service; import java.net.URL; class VremenskiKlijent { public static void main(String args[ ]) throws Exception { URL url = new URL("http://localhost:9876/vrijeme?wsdl"); QName qname = new QName("http://primjer1.servisi/", "VremenskiServerImplService"); Service service = Service.create(url, qname); VremenskiServer eif = service.getPort(VremenskiServer.class); System.out.println(eif.getVrijeme()); } }

Objašnjenje: prvi korak je definiranje URL adrese WSDLa za naš servis. URL objekt će nam kasnije biti potreban za definiranja reference prema servisu. Nakon toga nam je potrebno XML kvalificirano ime Web servisa (XML namespace) u obliku objekta Qname čiji konstruktor prihvaća dva argumenta i to URI Web servisa i ime Web servisa; oboje je definirano u WSDLu. Razlika između pojma URL (Uniform Resource Locator) i URI (Uniform Resource Identifier) je u tome što URL obavezno specificira lokaciju, a URI ne

11

nužno. Kada imamo URL i kvalificirano ime Web servisa možemo deklarirati objekt klase Service pomoću statičke metode create koja kao argumente uzima spomenute objekte. Ovaj objekt predstavlja naš servis kao resurs. On može imati jednu ili više krajnjih točaka (port-ova). (Jedina) krajnja točka našeg servisa je objekt tipa VremenskiServer, što je u kodu zapravo Java sučelje servisa – SEI. Objekt kreiramo metodom getPort našeg objekta service. Iako objekt ima tip Java sučelja u kojem je definiran samo predložak metoda, sam objekt sadrži sve što mu je potrebno za pozivanje operacija koje su definrane u WSDLu u odjeljku portType, a o tome se brine metoda getPort. Nakon toga možemo pozvati našu operaciju kao metodu svakog drugog objekta.

SOAP poruke Sada ćemo pogledati što se događa „ispod haube“, odnosno kakve poruke razmjenjuju klijent i Web servis. Budući da smo napravili SOAP bazirani Web servis razmjenjuju se SOAP poruke. SOAP inače dolazi u dvije verzije 1.1 i 1.2. Razlike su u pravilu minorne za programera Web servisa. Struktura standardne SOAP poruke prikazana je na sljedećoj slici:

SOAP omotnica (Envelope) je korjenski element SOAP poruke. <?xml version="1.0"?> <soap:Envelope xmlns:soap="http://www.w3.org/2001/12/soap-envelope" soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding"> ... </soap:Envelope>

Ovaj element definira XML dokument kao SOAP poruku. Element Envelope sadrži najčešće dva atributa i to:

• xmlns:soap="http://www.w3.org/2001/12/soap-envelope" – ovaj atribut mora uvijek biti prisutan. Ovdje definiramo namespace pod ključem soap (može biti i

SOAP Poruka

(Opcionalno) SOAP zaglavlje

(Obavezno) SOAP tijelo

SOAP Omotnica

(Opcionalni) SOAP privici

12

neki drugi, npr. soapenv, S). To je ključ koji ćemo kasnije koristiti kao prefiks elementima i atributima koji pripadaju SOAP vokabularu. Iz URI-a vidimo da se jasno radi o SOAPu.

• soapenv:encodingStyle="http://www.w3.org/2001/12/soap-encoding" – ovaj atribut se može i ne mora pojaviti i definira tipove podataka korištene u dokumentu. U ovom primjeru odabrano je standardno SOAP kodiranje, definirano URI-em.

SOAP zaglavlje (Header) ako postoji mora biti prvi element u SOAP omotnici i definira aplikaciji specifične informacije o SOAP poruci. U elementima unutar SOAP zaglavlja mogu se pojaviti tri karakteristična atributa: soap:mustUnderstand, soap:actor i soap:encodingStyle. mustUnderstand može imati vrijednost 0 ili 1, može se primjeniti na sam Header element ili elemente koje sadrži, a znači da li je primatelj dužan (1) ili ne (0) obraditi, odnosno prepoznati dotični element i sve što je sadržano u njemu. actor atribut se koristi za adresiranje elemenata specifičnom primatelju, budući da ista SOAP poruka može proći preko nekoliko točaka na svom putu. Vrijednost atributa je URI. encodingStyle ima jednaku primjenu kao i kod SOAP omotnice. SOAP tijelo poruke (Body element) sadrži konkretnu SOAP poruku namjenjenu krajnjoj točci. Elementi i atributi koje Body element sadrži mogu biti kvalificirani raznim namespace-ima. Body element može opcionalno sadržavati jedan Fault element u kojem su naznačene greške. U našem primjeru klijent šalje poruku zahtjeva u obliku HTTP zahtjeva u čijem tijelu je SOAP poruka. POST http://127.0.0.1:9876/vrijeme HTTP/1.1 Accept-Encoding: gzip,deflate Content-Type: text/xml;charset=UTF-8 SOAPAction: "" User-Agent: Jakarta Commons-HttpClient/3.1 Host: 127.0.0.1:9876 Content-Length: 222 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:prim="http://primjer1.servisi/"> <soapenv:Header/> <soapenv:Body> <prim:getVrijeme/> </soapenv:Body> </soapenv:Envelope>

HTTP zahtjev ima svoj karakteristični format. Prva linija zahtjeva definira HTTP metodu, u ovom slučaju je to POST metoda, što je uobičajeno za zahtjeve koji se šalju dinamičkim resursima i Web servisima. U ovom slučaju je POST obavezan i ne možemo korisiti GET metodu. Razlog je taj što u tijelu HTTP zahtjeva šaljemo SOAP poruku, a to nam omogućuje samo POST metoda, za razliku od GET metode koja ne sadrži tijelo tijelo. Nakon toga, u prvoj liniji slijedi adresa resursa i verzija HTTP protokola. Nakon prve linije slijede HTTP zaglavlja, odnosno parovi ključeva i vrijednosti odvojenih dvotočkom (:). Redoslijed zaglavlja nije bitan. Ponekad HTTP zahtjev može imati Accept ključ s raznim vrijednostima. Ova zaglavlja služe tome da obavijeste primatelja (Web servis) koje formate odgovora je pošiljatelj (klijent Web servisa) spreman prihvatiti. Format je opisan MIME (Multiple Internet Mail Extension) kombinacijama tipova i podtipova odvojeno kosom crtom (npr. text/xml znači

13

da je prihvatljiv bilo kakav XML dokument; application/soap znači da je prihvatljiv SOAP dokument). Ključ SOAPAction se često može naći u HTTP zaglavljima zahtjeva prema Web servisima i vrijednost može biti prazna, ali ovdje može biti i operacija Web servisa koju pozivamo. Na strani Web servisa se HTTP zahtjev obrađuje na način da se izdvoji SOAP dokument, iz njega se izdvoji identifikator operacije Web servisa koju klijent poziva, poziva se konkretna metoda u programskoj logici Web servisa i na temelju rezulata te metode se formira HTTP poruka odgovora koja sadrži SOAP poruku. U nastavku slijedi poruka odgovora iz našeg primjera. HTTP/1.1 200 OK Transfer-encoding: chunked Content-type: text/xml;charset="utf-8" <?xml version="1.0" ?> <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> <S:Body> <ns2:getVrijemeResponse xmlns:ns2="http://primjer1.servisi/"> <return>Sat Oct 31 19:28:53 CET 2009</return> </ns2:getVrijemeResponse> </S:Body> </S:Envelope>

Kao što je rečeno odgovor je HTTP poruka odgovora koja u tijelu sadrži SOAP poruku odgovora. Prva linija HTTP poruke je statusna linija koja se sastoji od verzije HTTP protokola, statusnog koda i pratećeg teksta. '200 OK' znači da je klijentov zahtjev uspješno obrađen. Slijedi nekoliko linija zaglavlja. SOAP poruka u tijelu HTTP zahtjeva sadrži odgovor Web servisa na klijentov zahtjev u formatu koji će klijentu biti poznat, kako je i predviđeno sklopljenim ugovorom među klijentom i servisom (WSDL).

14

Terminologija WCFa Implementacija Web servisa pomoću WCFa je prilično jednostavna. Za to nam je potreban .NET Framework minimalno verzije 3.0 i Visual Studio 2008 ili pak Visual Studio 2005 plus dodatak .NET Framework 3.0. WCF servis sastoji se od 3 dijela: (1) servisa, (2) jedne ili više „krajnjih točaka“ (endpoint) i (3) okruženje u kojem pružamo servis. Servis je zapravo klasa napisana u .NET kompatibilnom programskom jeziku. Klasa sadrži jednu ili više metoda koje su dostupne kroz WCF servis. Servis isto tako može imati jednu ili više krajnjih točaka koje predstavljaju komunikacijski kanal klijenta prema servisu. Krajnja točka servisa je resurs na mreži kojemu klijenti mogu poslati poruku formatiranu na način utvrđen ugovorom sklopljenim između servisa i klijenta. Da bi klijent uspješno poslao poruku na krajnju točku servisa, treba znati tri elementa koji određuju krajnje točke. Ove elemente Microsoft naziva akronimom ABC, a to su:

• A = address, adresa – definira mjesto na mreži gdje klijent treba poslati poruku. • B = binding, poveznica – definira kanal koji se koristi sa komunikaciju s krajnjom

točkom. Kanali su vodovi kroz koje prolaze sve poruke unutar WCF aplikacije. Kanal je sastavljen od niza binding elemenata.

• C = contract, ugovor - definira mogućnosti i opcije koje nudi krajnja točka, odnosno operacije koje nudi i format poruka koje zahtijeva. U ugovoru su pojedine operacije povezane s metodama klase koja implementira krajnju točku, a definirani su i parametri koji se šalju kao ulaz i izlaz.

Ovo isto možemo opisati na sljedeći način: A je gdje, B je kako, a C je što. WCF sustav možemo prikazati sljedećom slikom. Budući da je tok poruka u pravilu obosmjeran, klijenti također implicitno predstavljaju krajnju točku.

WCF klijent

Krajnja točka

A B C

WCF servis

Krajnja točka

C B A

Krajnja točka

C B A

Krajnja točka

C B A

Poruke

15

Na kraju, okruženje u kojem pružamo servis je mjesto gdje se servis „nalazi“ kada je u pogonu. Okruženje sačinjava aplikacijska domena i proces u kojem servis egzistira na operacijskom sustavu. Domaćin WCF servisu može biti bilo koji samostalan proces, Web poslužitelj, klijentska desktop aplikacija itd. Da bi servis bilo moguće otkriti dinamički, servis može uključiti tzv. infrastrukturnu krajnju točku koja se naziva Metadata Exchange (MEX) krajnja točka. Ova točka je dostupna klijentima i preko nje mogu dohvatiti ABCove servisa u obliku WSDL dokumenta. MEX krajnja točka se poziva kada u Visual Studiu dodajemo referencu prema servisu (Add Service Reference) ili kada koristimo alat svcutil.exe za automatsko generiranje klijentskog koda (sličan alat ima i Java). MEX krajnja točka, kao što je rečeno, vraća WSDL i na temelju njega se automatski generira klijentski kod (proxy klasa s potpisom operacija koje nude krajnje točke) i potrebne konfiguracijske datoteke.

Implementacija SOAP Web servisa pomoću WCFa Na najvišoj razini, implementirati Web servis znači napisati kod koji implementira nekakvu mogućnost, taj kod postavimo u proces operacijskog sustava, i nakon pokretanja proces čeka na zahtjeve i daje odgovore klijentima. U praksi, to znači „napisati“ .NET klase i, slično kao što smo u Java klasama ključna mjesta naznačili s odgovarajućim anotacijama, tako i ovdje na ključna mjesta postavimo odgovarajuće atribute iz System.ServiceModel namespace-a. Prvo ćemo implementirati ugovor WCF servisa. using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; namespace WCFServis { [ServiceContract] interface IVremenskiServer { [OperationContract] string getVrijeme(); } }

Objašenjenje: kao i kod Java programskog jezika servis započinjem implementacijom sučelja odnosno ugovora u dijelu programiranja. Sučelje koje predstavlja ugovor naznačujemo atributom [ServiceContract]. S gledišta WSDL dokumenta ovaj atribut definira portType element. [OperationContract] atribut označuje metode koje se mogu pozivati kroz sučelje Web servisa. S gledišta WSDLa [OperationContract] definira Operation i Message elemente.

16

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace WCFServis { class VremenskiServer : IVremenskiServer { public string getVrijeme() { return new DateTime.UtcNow.ToString(); } } }

Objašnjenje: nakon što smo napravili sučelje, moramo ga poduprijeti implementacijom poslovne logike. Izrađujemo klasu koja implementira sučelje gdje konkretiziramo našu metodu koja će biti na raspolaganju klijentima kroz Web servis. using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.ServiceModel.Description; namespace WCFServis { class PokreniVremenskiServer { public static void Main() { ServiceHost serviceHost = new ServiceHost(typeof(VremenskiServer), new Uri("http://127.0.0.1:9876/vrijeme")); serviceHost.AddServiceEndpoint(typeof(IVremenskiServer), new BasicHttpBinding(), ""); ServiceMetadataBehavior behaviour = new ServiceMetadataBehavior(); behaviour.HttpGetEnabled = true; serviceHost.Description.Behaviors.Add(behaviour); serviceHost.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex"); serviceHost.Open(); Console.WriteLine("Pritisni <ENTER> za kraj.\n\n"); Console.ReadLine(); serviceHost.Close(); } } }

Objašnjenje: kada smo implementirali servis u potpunosti (sučelje i implementacija sučelja) možemo pristupiti definiranju krajnje točke. Kao što je ranije prikazano, krajnju točku definiramo akronimom ABC (Address, Binding, Contract). U tu svrhu ćemo koristiti klasu ServiceHost i prvenstveno metodu AddServiceEndpoint. ServiceHost klasu, odnosno instance te klase, koristimo kada želimo konfigurirati i izložiti servis tako da ga klijenti mogu koristiti, ali bez upotrebe IISa (Internet Information Services). U našem slučaju pri instanciranju ServiceHost objekta koristimo konstruktor koji kao argumente uzima tip

17

servisa (naš tip je VremenskiServer) i baznu Web adresu na kojoj ćemo obznaniti naš servis. Sljedeći korak je kriranje krajnje točke našeg servisa, gdje koristimo spomenutu metodu AddServiceEndpoint. Mi ćemo koristiti verziju metode koja kao argumente uzima sljedeće – tip sučelja servisa (Contract), poveznicu (Binding) i na kraju adresu (Address). Tip sučelja servisa je jasno naznačen kao tip IVremenskiServer, kao poveznicu (binding) odabiremo objekt tipa BasicHttpBinding koji je osnovni izbor jer funkcionira sa većinom sustava koji implementiraju XML Web servise i ima ugrađenu svu potrebnu funkcionalnost za takve slučajeve. Adresu u našem slučaju ostavljamo praznu jer želimo da krajnja točka preuzme baznu adresu definiranu kroz ServiceHost objekt. Metodom Open, ServiceHost objekta startamo proces servisa koji sada čeka zahtjeve klijenata. Ovako postavljeni Web servis ne nudi nikakve informacije za klijente o tome kako koristiti servis, odnosno ne možemo nikako dobiti WSDL servisa na temelju kojega klijent može doznati detalje o pristupu servisu ili automatski generirati klijentski kod i konfiguracijske datoteke za pristup servisu. U tu svrhu, kako smo ranije istaknuli, trebamo dodati MEX krajnju točku. Dio koda koji dodaje MEX krajnju točku, istaknut je u prethodnom primjeru različitom bojom. Prvo dodajemo objekt tipa ServiceMetadataBehavior našem ServiceHost objektu, odnosno dodajemo novu funkcionalnost (ili „ponašanje“) servisu na temelju kojega će servis moći ponuditi WSDL klijentima. Nakon toga, eksplicitno dodajemo MEX krajnju točku koja ima ugovor tipa IMetadataExchange, poveznica je standardna MEX HTTP poveznica (binding), a adresa je simbolično „mex“. Do MEX krajnje točke možemo sada pristupiti preko adrese: http://127.0.0.1:9876/vrijeme?wsdl. Važno je uočiti da smo naš ServiceMetadataBehavior objekt modificirali tako da smo omogućili korištenje HTTP GET metode (HttpGetEnabled = true) što znači da ćemo našoj MEX krajnjoj točki moći pristupiti i iz preglednika. Sada je sve spremno za korištenje Web servisa i možemo pristupiti implementaciji klijenta. using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; namespace WCFKlijent { class VremenskiKlijent { static void Main(string[] args) { ChannelFactory<IVremenskiServer> myChannelFactory = new ChannelFactory<IVremenskiServer>(new BasicHttpBinding(), new EndpointAddress("http://127.0.0.1:9876/vrijeme")); IVremenskiServer wcfClient = myChannelFactory.CreateChannel(); string result = wcfClient.getVrijeme(); Console.WriteLine(result); Console.WriteLine("Pritisni <ENTER> za kraj.\n\n"); Console.ReadLine(); } } }

18

Objašnjenje: u našem primjeru klijentski dio smo napravili u potpunosti ručno kroz kod, a kasnije ćemo pokazati kako se ovaj postupak može automatizirati i vidjet ćemo način pristupa servisu kombinacijom koda i konfiguracijskih datoteka. Isto kao što i krajnja točka mora definirati ABC, klijent mora znati ABC da bi pristupio servisu i mora ugraditi ove elemente u svoj kod. Adresa servisa je jednostavna. To je adresa na kojoj smo obznanili servis u standardnom formatu. Poveznica krajnje točke definira komunikacijski mehanizam kroz koji je servis izložen, npr. NetTcpBinding, WsHttpBinding ili BasicHttpBinding. Ugovor se precizno definira kroz XML dokument koji servis razumije. Prvi „korak“ ugovora izražava se kroz spomenute atribute [ServiceContract] i [DataContract] kojima naznačujemo dijelove koda u klasi ili sučelju, a WCF prevodi taj dio (drugi korak) u XML, odnosno WSDL koji će klijenti moći korisiti, pogotovo ako su razvijeni u drugoj tehnologiji. Dakle, prvi dio implementacije klijenta je osigurati klijentu sučelje servisa. Sučelje se dijeli između servisa i klijenta. Sintaktički, C# sučelje, odnosno ugovor se razlikuje od WSDLa, ali značenje im je zapravo isto. Odnosno, oboje precizno opisuje kako pristupiti servisu, koji su nazivi operacija i koji su parametri. Drugi dio je otvoriti kanal prema servisu, a to radimo pomoću objekta klase ChannelFactory. U uglatim zagradama specificiramo tip kanala koji želimo, a konstruktor uzima dva parametra, a to su poveznica i to BasicHttpBinding i adresa kao objekt klase EndpointAddress. Kada smo inicijalizirali „tvornicu“ kanala, metodom CreateChannel napravimo kanal koji dobiva tip našeg C# sučelja (IVremenskiServer). Sada možemo preko kreiranog objekta tipa IVremenskiServer pozivati metode servisa kao da se zapravo nalaze lokalno. Automatsko generiranje klijentskog koda Postoji i jednostavniji i brži način korištenja servisa, odnosno razvijanja klijenta Web servisa, a to je automatsko generiranje pomoću alata koje nudi gotovo svaka platforma. Automatsko generiranje u .NETu možemo napraviti na dva načina: (1) pomoću Visual Studia opcijom Add Web Reference ili pomoću alata svcutil.exe koji dolazi s .NET platformom. Oba načina daje iste rezultate i jedini kod koji trebamo napisati na klijentskoj strani u tom slučaju je sljedeći: using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace WCFKlijentAuto { class VremenskiKlijent { public static void Main() { VremenskiServer.VremenskiServerClient proxy = new VremenskiServer.VremenskiServerClient(); string result = proxy.getVrijeme(); Console.WriteLine(result); Console.WriteLine("Pritisni <ENTER> za kraj.\n\n"); Console.ReadLine(); } } }

19

Kao što vidimo, da bismo uspostavili vezu prema servisu potrebna nam je jedna linija koda. Da bismo to postigli koristimo opciju Add Service Reference.

Jedino što trebamo napraviti je upisati adresu našeg servisa, odabrati predloženi servis, dati mu ime i dalje će sve Visual Studio napraviti za nas. Iza kulisa Visual Studio generira dvije datoteke. Jedna je konfiguracijska datoteka app.config, a druga datoteka je datoteka Reference.cs u kojoj se nalazi kod potreban za korištenje Web servisa, sličan onome što smo mi pisali. Ova klasa naziva se još proxy klasom. U kombinaciji te dvije datoteke dobivamo mogućnost jednostavnog spajanja na servis. Isti rezultat smo mogli dobiti korištenjem alata svcutil.exe koji se nalazi obično u direktoriju C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin. Njega koristimo na sljedeći način: svcutil http://127.0.0.1:9876/vrijeme?wsdl -config:app.config -out:generatedProxy.cs Adresa je obavezna, dok ostale opcije nisu obavezne.

20

Popis literature Martin Kalin, Java Web Services: Up and Running, 1st Edition, O'Reilly Media, Inc., USA, 2009. Steve Resnick, Richard Crane, Chris Bowen, Essential Windows Communication Foundation, Addison-Wesley, USA, 2008. Bill Evjen, Scott Hanselman, Devin Rader, Professional ASP.NET 3.5 SP1 Edition in C# and VB, Wiley Publishing, Inc., USA, 2009. http://www.wikipedia.org