SOAP
A SOAP, mint "XML-közeli" varázslás, egyike napjaink felkapott technológiáinak, mint ahogy az XSLT, a Java, vagy maga az XML is – csak hogy a legtúllihegettebbeket említsük. Ennek megfelelően már most leszögezném, hogy nemcsak ez, de semelyik* más szabvány, alkalmazás vagy programnyelv sem fogja megváltani a világot, és semelyiket nem kell mindenre használni, csak azért mert "az jó" és "menő". Mindazonáltal a SOAP-nak megvan a maga létjogosultsága, és sok mindenre tényleg alkalmas – sőt, netán a legalkalmasabb. Az alkalmasság rendkívüli jelentőséggel bír: a mai RAD eszközökkel támogatott szoftverfejlesztés elvárja a villámgyors fejlesztéseket, így gyakran az nyer, aki a legmegfelelőbb technológiát választja, hiszen azzal készül el a leghamarabb és legköltséghatékonyabban.
Ha okosat akarnánk mondani, azt mondanánk, hogy a SOAP (Simple Object Access Protocol) egy elosztott processzek közötti kommunikációra szolgáló, XML alapú objektumhozzáférési protokollt ír le. (Ilyesmiből van nekünk elég: CORBA, RMI, ésatöbbi. A SOAP ezeknél talán jobban "lightweight", továbbá XML, továbbá flexibilisebb, továbbá tök más.) Mivel azonban nem okosan akarjuk mondani, hanem érthetően, inkább fogalmazzunk így: a SOAP célja az, hogy két, tipikusan szerveralkalmazás – igen gyakran website – a lehető legegyszerűbben, mégis szabványosan és biztonságosan közöljön egymással adatokat.
Mielőtt tovább fejtegetnénk az elméletet, vegyünk egy gyakorlati feladatot! Adott egy weblap, amelyik összegyűjt más weboldalakon értékesíteni kívánt ingatlanokat. Mindezt oly módon teszi, hogy megkéri ezeket a bizonyos site-okat, ugyan küldjék már el neki rendszeresen aktuális kínálatukat: az ingatlan leírását, árát, a helység nevét, ahol található, illetve az URL-t, ahol a részletesebb leírás és egyéb adatok is elérhetőek. Mindezt persze jó lenne automatizálni, az adatbázist minél frissebben tartani, teszem azt, mikor az adminisztrátor az X másik oldalon felrakja a saját hülyeségét, akkor erről rendszere rögvest küldjön egy behívást a gyűjtőszerverre a megfelelő adatokkal.
Fontoljuk meg ezt a dolgot és a felmerülő problémákat! Ez nem elmélet, ez a való világ ((c) G.Z.): adott pár tucat tök más adatbázison és karakterkészleten, tök más programnyelven működő weblap. Adott pár tucat döglusta programozó, akik 5 sornál többet nem hajlandóak beíni sehova, meg amúgy sem értenek semmihez. Ha ezeknek a népeknek elkezdünk protokollokat definiálni, hogy például https post, és a mezők így kódolva meg úgy kódolva és ilyen néven meg olyanon, továbbá enctype meg content-type meg 443-as port meg ilyenek, akkor meg vagyunk lőve: a JSP-s csávónak baja van az SSL-lel, a PHP-s nem tud post-olni, mert hülye hozzá, a CGI-s nem bírja a visszatérési értékeket enkódolni, mert tegnap kezdett programozni, a SAP gyerek meg azt mondja, neki ilyen támogatás nincs, és ez a válasz 50000Ft + áfá-ba került, a doksit meg 200-ért olvassa majd el, mert hosszú.
Nos, tipikusan egy ilyen helyzetet menthet meg a SOAP, melynek alkotói a következőkből indultak ki:
–a kommunikációban részt vevő két (vagy több) fél mindegyike valami értelmes nyelvet illetve rendszert használ, amelyhez van SOAP támogatás
–mindkét fél valamilyen módon – jellemzően legalább http-n keresztül – eléri a másik alkalmazását (szerverét).
Ezek a feltételek szinte mindig adottak, ahol meg nem, az most legyen másvalaki problémája.
Technikailag a SOAP egy metódushívás XML-be történő csomagolása és továbbítása, amit mindkét fél egyformán értelmez. (Itt álljunk meg. Fogjuk ezt fel. Kész? Akkor mehet tovább.) Mindazonáltal mi magas ívben tojunk arra, hogy ennek az XML-nek a formátuma hogyan is néz ki: "nem érdekel ez most" ((c) Dallas.mpg), ezt is mondhatnánk, kitalálták valakik, megy, kalap. Hogy ki és hogyan csinálja meg magát az adattovábbítást, tehát általános esetben a http hívást, megint nem a mi gondunk, a könyvtár (osztály, modul stb.) tudja, és kész. Nem foglalkozunk tehát a DTD-vel vagy a http protokollal: ez a leírás sem szól igazából másról, mint arról, hogy hogyan kell használni a SOAP-ot, és ez jól is van így. (Akit az XML specifikáció érdekel, ott a link a cikk végén.)
Térjünk vissza az eredeti példára, és irkáljunk egy kis pszeudokódot! Vegyük a kliensoldalt, ahol éppen most töltöttek fel egy új ingatlant a jónépek az adatbázisukba, és tolnák át hozzánk:
// ...adat lokális részét lekezeltük,
// és akkor innen SOAP-olunk:
soap_setup_nekiküldjük (távoli_szerver)
// merugye azonosítani kell a másik felet
soap_add_ingatlan (url, leírás, ár, város)
// nesze az adataid, majom
if (soap_sikerült) örülünk() else nem_örülünk()
// kegyetlen hibakezelés
Tulajdonképpen az igazi, tehát nem pszeudokód sem lesz sokkal hosszabb ennél, ami egy vidám dolog, az egyszerűség ugyanis mindig sokat segít azon, hogy valami ténylegesen elkészüljön, és ne haljon meg a tervező monitorán.
A pszeudokódból látszik, hogy a SOAP hívás egy metódus/szubrutinhívás formájában jelenik meg. Ez azt jelenti, hogy – akárcsak a már említett RMI esetén is – a SOAP lényegében eltakarja előlünk azt, hogy a meghívott rutin/objektum nem a saját gépünkön éldegél. Ez megint csak jó: nem kell olyannal foglalkozni, hogy az adatokat konvertáljuk, és egyáltalán par excellence adatkezeléssel sem kell foglalkoznunk, a programlogikába egy metódushívás sokkal jobban beillik, mint mindenféle adatkonverzió és egyéb szüttyögések.
Szerveroldalon egy picit már másképpen fest a dolog. Míg a kliensnél csak egy behívás van, a szerverre oda kell varázsolni egy SOAP kiszolgálót – őt úgy hívják, hogy SOAP proxy – ami tulajdonképpen egy dispatcher, a behívások leosztásáért felelős. Ide hegesztjük továbbá a SOAP alkalmazást, aminek a SOAP proxy osztja az észt, így az alkalmazás a klienshez hasonlóan úgy látja, mintha a behívás helyileg jött volna (tulajdonképpen úgy is jött, a SOAP proxy a SOAP alkalmazás behívója, az meg helyileg van jelen. Egyszerűbb esetekben a proxy és az alkalmazás egyetlen egység is lehet.)
A szerver pszeudokód:
// ...én vagyok a kis SOAP...
metódus soap_add_ingatlan (url, leírás, ár, város) {
// engem hív a soap server
sok_hülyeség_eltárolása (url, leírás, ár, város)
// csinálok valamit
return valami_idétlen_hibajelzés_vagy_más()
// visszaugatok
}
Ennyi. TADA.WAV. És akkor végre valami izgalmas: írjuk meg a fenti alkalmazást úgy, hogy copy-paste az Impulzusból, és egy az egyben működik is! Definiáljuk a feladatot: generálunk egy SOAP proxy-t, ami egy daemon processz lesz, és semmit sem csinál, csak fogadja az adatokat, és visszaadja a beadott paraméterek, mint stringek teljes hosszát (hogy csináljon is valamit, azér). A kliens ezt hívja meg, és kiírja a visszakapott értéket, tehát a hosszt. BOT OH.
Kérem szépen: perl-t fogunk használni. A példa futtatásához szükség lesz valami Linux jellegű gépre, perl futtatóra (bwahaha) meg egy-két (CPAN) modulra. Feltételezzük azt, hogy egy root jogokkal ellátott Linux terminál előtt ülünk: akinek ilyen nincs, az programozzon fejben, vagy kérjen meg valakit, aki ilyen jellegű dolgokat tud neki biztosítani. (A root jog csak akkor kell, ha egyes szükséges modulok hiányoznak, amiket installni kell – ezeket root jog nélkül is fel lehet persze rakni, de azt most itt inkább nem részletezzük, oldják meg a kollégák önerőből!).
Először is szükségünk lesz pár SOAP modulra. A triviális install:
perl -MCPAN -e shell
install SOAP
install SOAP::Lite
install SOAP::Transport::HTTP::Daemon
Szuper, ennyi volt (az utolsó sor lehet nem is kell, de franc emlékszik). A default install többnyire megfelel, aki komolyan akarja használni az később úgyis módosít majd ezen. A https-t érdemes kérni, bár a példához nem kell (OpenSSL-t igényel ez az opció).
Írjuk meg először a klienst! Legyen a file neve client.pl, majd úgy lehet futtatni, hogy perl client.pl (értelemszerűen). Tételezzük fel, hogy a szerver is itt fog futni, vagyis a localhoston, méghozzá az 12345, tehát nem privilegizált porton.
Ehun a client.pl tehát:
use SOAP::Lite; # ő csinálja a mindent
my $ingatlan = {
'url' => 'http://www.kepeslap.com',
'leiras' => 'Ez nem is egy ingatlan, megszivattunk',
'ar' => 1000000,
'varos' => 'Bivalybasznád'
} ; # Tesztadatok ezek itt
my $soap = SOAP::Lite # SOAP objektum létrehozása
-> uri('/Demo') # SOAP uri, lásd lejjebb
-> proxy('http://localhost:12345/'); # a másik oldal proxy címe
my $result = $soap->add_ingatlan ($ingatlan); # erről van nagyba szó, ez már egy "remote call"
unless ($result->fault) { # nem-e volt-e error (e)
print $result->result(); # ezt mondotta a másik oldal
} else {
print join " n", # bwahahaha
$result->faultcode,
$result->faultstring,
$result->faultdetail; # Error volt, kiírunk mindent amit tudunk a hibáról
}
Ez azért eléggé egyszerűnek tűnik, nemdebár? Ha úgy vesszük, maga a SOAP kommunikáció 4 sor (amit egy sorba is írhattunk volna, csak nem akartunk összekeverni senkit). Remek! Most nézzük a szervert (server.pl), utána elmagyarázzuk, ami esetleg még nem világos.
use SOAP::Transport::HTTP; # Ezt fogjuk júzolni: SOAP HTTP-n keresztül
$daemon = SOAP::Transport::HTTP::Daemon
-> new (LocalPort => 12345)
-> dispatch_to('Demo'); # indítunk egy daemont az 12345 porton, hívjuk a Demo csomagot, lásd később
print "SOAP server itten futtik: ", $daemon->url, " n";
$daemon->handle; # meeennnyél!
# Tkp. ez itt felül a proxy, alul meg most jön az alkalmazás
package Demo; # Fantáziadús név... a SOAP proxy ide "diszpécsel"
sub add_ingatlan { # vágjátok, ezt hívja a másik oldal
my ($class, $f) = @_; # az osztály az osztály, ha nem érted, "man perlobj"
return length join '', values %$f; # visszaadjuk a hosszát
}
Ez sem volt nagy varázslat! A SOAP proxy tulajdonképpen szintén 4 sor (szintén lehetne egy), a SOAP alkalmazás meg annyira kicsi, hogy hű: paraméterátvétel, adatvisszaadás, mintha lokálisan hívnák, ennyi volt.
Most jön az irodalom-szakkör: forráselemzés!
Tekintsük meg először a klienst!
1.use SOAP::Lite;
Ezen nincs mit magyarázni, előkapjuk a SOAP modult. (Ezek a ::Lite meg ::Simple dolgok egyébként nagyon jók, pl. XML::Simple vagy MIME::Lite szintén szuperul használható).
2.A $ingatlan-os sorral most ne foglalkozzunk, csinálunk egy asszociatív tömböt a tesztadatainknak.
3.A $soap objektum létrehozásakor két paraméterrel varázsolunk: a "proxy" egyértelmű, ez simán megadja a SOAP proxy "fizikai" helyzetét. A SOAP nagy varázslásokat tud, sokféleképpen tud kommunikálni, még e-mailen keresztül is – most egy sima http porton keressük a proxynkat. Az uri paraméter már érdekesebb: ez az uri hasonló uri, mint amit a web-es URL-ek leírásánál megszoktunk, a SOAP proxynak ugyanis saját "namespace"-e van, tehát például esetünkben több SOAP alkalmazást is varázsolhatnánk a másik oldalra, amelyeket ezzel az uri-val azonosíthatnánk. (Ez azért jó nekünk, mert így elég egyetlen proxy-t készítenünk: az alkalmazásokat az uri függvényében a proxy hívogatja. Egy SOAP proxy, sok alkalmazás.)
4.Miután létrejött a remek SOAP objektumunk, behívjuk a másik oldalt. Ezt simán úgy tesszük meg, mintha a másik oldalon található metódus a SOAP objektumunk metódusa lenne, tehát úgy is felfoghatjuk, hogy a visszakapott objektum a "másik szerver egy részének helyi reprezentációja".
5.A visszaadott $result objektum egy új objektum, amit a SOAP::Lite gyártott nekünk. Ebből lekérdezhetjük, hogy történt-e valami hiba, illetve a result() metódus visszadobja nekünk a másik oldali return értéket.
Tetszikérteni? Ez persze egy eléggé leegyszerűsített példa, mert olyannal nem foglalkozunk úgy igazán, hogy milyen SOAP verziót beszél a másik oldal, hogy egyáltalán van-e ott olyan metódus, amit hívni akarunk – ha nincs, az ellen nem véd. Ezek most nem is fontos dolgok, itt még nem tartunk agyilag, aki akarja, az utánanéz.
Tekintsük a szervert!
1.use SOAP::Transport::HTTP;
A SOAP::Transport-on belül van egy kazal osztály, nekünk most a HTTP-re lesz szükségünk.
2.$daemon = SOAP::Transport::HTTP::Daemon ...
A SOAP::Transport::HTTP::Daemon egy ilyen kis segítség, ami arra való, hogy egy Daemon processz kulmináljon ezerrel a gépünkön egyetlen sorból. Ha keményen nyomjuk a SOAP-ot, valószínűleg más transport agent-et (ágenst, vaze, ágenst) fogunk használni, pl. egyszerűbb esetben a CGI-t, kicsit keményebb esetben mondjuk az Apache SOAP modulját. Esetünkben megmondjuk a helyi portot (12345), illetve a dispatchernek megmondjuk, hogy a Demo csomagnak nyomja tovább az ide érkező hívásokat. Ez kellemesen perl-es megoldás, annyit kell csak tennünk, hogy a megfelelő modult use-oljuk, annak metódusait a másik oldalról meg szépen meghívhatjuk. Esetünkben nem use-oljuk a Demo modult, hanem beleírjuk ebbe a forrásba. Ez van, így egyszerűbb: hiába no, a perl megengedi a package-váltást egy file-on belül.
3.$daemon->handle;
Ezzel indítjuk el a Daemont. Innentől fut, mint egy állat, figyel a HTTP szerver a megadott porton.
4.package Demo;
Ez már maga a SOAP alkalmazás. Minden $method hívás, ami a Demo uri-ra jön, az mint Demo::$method hívás hajtódik végre. Nagy királyság, kicsit egyszerűbb, mint a Java-s reflection API, nem?J
5.sub add_ingatlan
Mint látható, a SOAP proxy megoldja, hogy a másik oldalon kiadott paraméterhívás mint teljesen "reguláris" perl metódushívás foganatosítódjon (bwahaha). A $class ne zavarjon senkit, ez egy perl "osztály", tehát az első paraméter az osztály/objektum referenciája (úgy közelítőleg). A return sor meg semmi más, összefűzi a paraméterek hosszát, és simán visszaadja. A visszatérési érték a SOAP proxy-nak megy, az csomagolja, és visszaadja a kliensnek.
Kérem, megokosodtunk, ennyi volt ez röviden. Namármost: lehet választani, hogy a következő számban miről legyen szó, legalábbis én általam írva. Néhány lehetőség, talán célszerű lenne ezeket megközelíteni:
A)SOAP 2, vagyis ennek a cikknek a folytatása, ami kicsit komolyabb dolgokról is szót ejt a SOAP kapcsán.
B)Class::DBI, egy olyan kis perl modul, aminek segítségével az embernek kissé EJB-s feelingje támad, csak éppen perlben programozik. Ennek kapcsán tizenegy és fél perc alatt készítenénk egy fórum (noticeboard) alkalmazást, és megtanulnánk SQL nélkül SQL-t használni.
C)XML::Simple, vagyis hogyan dolgozzunk fel egyszerű XML file-okat, például konfig célokra – egyúttal pár szót az XML-ről is ejthetünk, elvégre van, aki annyira sötét, hogy még HTML-t sem tud szerkeszteni. :)
Végezetül kérjük a tisztelt olvasókat, hogy egyrészt jelezzenek vissza a cikkel kapcsolatban (teszem azt, jó volt-e vagy nem) ide: inyl@impulzus.sch.bme.hu, illetve válasszanak a fenti ABC pontokból, avagy javasoljanak egyéb témát! A feltett kérdésekre illetve elejtett megjegyzésekre a következő számban talán még válaszolunk is, ha más nem is, de kigúnyoljuk a kérdezőket. :-}
– Inzl
Olvasnivaló:
SOAP 1.1 szabvány: http://www.w3.org/TR/SOAP/
SOAP gyorstalpaló 1:
http://www.perl.com/pub/a/2001/01/soap.html
SOAP gyorstalpaló 2:
http://www.perl.com/pub/a/2001/04/24/soap.html