středa 26. ledna 2005

XML-RPC a možná cesta pro bohatší webové aplikace

V poslední době poměrně často diskutuje s Michalem o problematice bohatšího grafického rozhraní webových aplikací. V podstatě se bavíme o současných možnostech (html, css, ECMAScript) a spekulujeme nad jednotlivými aspekty té či oné techniky, která by připadala v úvahu. V první řadě je třeba poznamenat, že diskutujeme multiplatformní řešení, které poběží na současných prohlížečích, bez nutnosti instalace jakéhokoliv rozšíření.

Díky tomuto omezení jsme přišly o silné kandidáty postavené na Flash playeru a také o XUL. Osobně vidím počátky RIA postavené právě na Fals playeru, kde se již dnes nabízí komerční Flex (Macromedia) tak open-source Lazslo. Dejme tomu, že bychom chtěli našim klientů poskytnout bohatší GUI, které by nabízelo uživateli větší komfort. Co k tomu budeme potřebovat?

Komponenty - v jednotě je síla

Pokud chceme sjednotit jednotlivé části webových aplikací, umožnit sdílení kódu a nastavit určité obecné chování (například skinovatelnost), pak se neobejdeme bez komponentního přístupu. Budeme muset nadefinovat základní sadu komponent, které poskytnou základní stavební materiál. Jaké komponenty by to měly být? Namátkou se jedná o button, input, combo box, tree, table, calendar, pane, tabbed pane. Celkově by se jednalo o zhruba deset až patnáct komponent.

Jedna věc je mít nadefinované statické komponenty a druhá je jejich oživení. Oživení komponent se mi jeví jako celkem problematické. Jednak budeme ovladače komponent na straně klienta a za druhé budeme potřebovat ovladače na straně serveru. Klientské ovladače by se měly starat o interakci s lokálním kontextem stránky např. uživatel stisknul tlačítko, je potřeba zavolat specifikovanou funkci (submit, validace). Ovladače komponent na serveru slouží k tomu, aby bylo možné držet stavy (rozbalený stromeček, vybrané datum v kalendáři) jednotlivých komponent přes requesty.

Takže máme jednotlivé komponenty a definovaná rozhraní pro jejich ovládání na klientské a serverové straně. Pokud chceme uživateli usnadnit práci, nebylo by na škodu pohrát si s request/response modelem. V podstatě se jedná o to, že jakákoliv dílčí změna na straně klienta, u které je nutný přenos dat na server, otravuje uživatele prodlevou při přenačtení celé stránky. Ve vztahu k našim komponentám jde o to, jak umožnit komponentám co nejefektivnější komunikaci.

Frame, iframe, object, xml-rpc?

Pomocí klientského skriptování můžeme celkem snadno využít "stand alone" prvky, které nabízí samotné HTML, jedná se především o rámce. Jednoduchým skriptem, který bude nastavovat URL pro daný frame lze vyvolávat GET požadavky bez nutnosti přenačtení celé stránky. Samozřejmě možností jak využít frame je daleko více. Nevýhodou framu je z mého pohledu těžkopádnost z hlediska volání serveru a zpracování odpovědi ve formě xml/html dat. Zvažme případ kdy na serveru vznikla nějaká chyba, komponenta bude muset vrácená data celkem složitě vyhodnotit.

Z mého pohledu se jeví jako vhodnější kandidát pro komunikaci komponent protokol XML-RPC. XML-RPC je protokol, který definuje jednoduchá pravidla pro vzdálené volání metod formou výměny XML dat s pevnou strukturou. Díky XML-RPC není problém zavolat jakýkoliv objekt vystavený přes XML-RPC. Existuje celá řada XML-RPC klientů, kteří umožňují komunikaci tímto protokolem, najdete je snad pro většinu dnešních platforem či jazyků (Java, .NET, Delphi, PHP atd.). V dnešních prohlížečích sice XML-RPC klient není přímo integrován, ale implementace není nijak složitá. Navíc bude existovat i volně dostupná implementace.

Výhodou XML-RPC je to, že lze přímo volat service vrstvu vystavenou přes XML-RPC. Nejsme tak nuceni definovat facade vrstvu, která musí v případě použití rámců explicitně mapovat jednotlivé GET/POST požadavky na volání service vrstvy. Následující jednoduché schéma ukazuje celý model komunikace klientských komponent s aplikační logikou prezentovanou service vrstvou.

Schéma ukazující komunikace přes XML-RPC.

Ukázka použití XML-RPC v JavaScriptu bez jakéhokoliv javascriptového frameworku

 
function getUserInfo(){
  var xml = "";
  xml +="<?xml version=\"1.0\"?>\n";
  xml +="<methodCall>\n";
  xml +="<methodName>MyService.getUserInfo</methodName>\n";
  xml +="<params>\n";
  xml +="<param><value><string>Roman</string></value></param>\n";
  xml +="<param><value><string>Pichlík</string></value></param>\n";
  xml +="</params>\n";
  xml +="</methodCall>\n";

netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");

  httpRequest = new XMLHttpRequest();      
  httpRequest.open("POST","http://myserver/xmlrpc");      
  httpRequest.send(xml);      	
  httpRequest.onload = onHttpResponse;              	            
}

function onHttpResponse(){
  xml = httpRequest.responseText;     
  alert(xml);
}