Nebudu přímo reagovat, ale spíš doplním vlastův blog DWR - AJAX knihovna pro remotování Java objektů o ,z mého hlediska hodně zajímavou ,vlastnost frameworku DWR 2.0, tj. Reverse AJAX.
Reverse AJAX je když dokážeme ze serveru volat clienta, toď velmi jednoduchá definice. A jak této skutečnosti dosáhnout? Existují 3 možné způsoby:
- polling - asi nejstarší dostupná varianta, klient se v pravidelných intervalech dotazuje serveru, zda není něco nového - nevýhoda je zřejmá, velmi často dotaz běží na server jenom proto, aby se dozvěděl, že nic nového není
- piggyback - dotaz na server se přiloží k nejbližšímu následujícímí regulérnímu požadavku na server - nevýhodou je, že se o změnách ze serveru můžeme dozvědět s poměrně velkým zpožděním
- comet - vytvoří se spojení se serverem, toto spojení se neuzavře a server jej využívá k odesílání dat klientovi v okamžiku, kdy má taková data k dispozici - výhodou je, že se klient o změnách dozví okamžitě a toto řešení nevyvolává nežádoucí provoz (blíže např. dagiho příspěvek)
DWR + Jetty = výkoný reverse AJAX
Nyní se dostaneme k řešení, které framework DWR dovoluje použít, pokud je nasazen v kontejneru Jetty 6.0. Ve verzi 6.0 totiž Jetty obsahuje podporu pro continuations, které umožňují "uspat" obsloužení aktuálního požadavku a vyvolat jej později (za podporyjava.nio
toto uspání neznamená uspání a blokování obslužného vlákna požadavku).Nyní se pomalu dostaneme k příkladu chat serveru. V první řadě si představíme třídu, která bude reprezentovat zprávu:
public class ChatMessage {
protected String nick;
protected String message;
public ChatMessage(String nick, String message) {
super();
this.nick = nick;
this.message = message;
}
public String getNick() {
return nick;
}
public String getMessage() {
return message;
}
}
Nic překvapujícího není k vidění. Následuje rozhraní pro příjemce nových zpráv a třída, která bude singleton a bude implementovat vstupní bránu pro všechny došlé zprávy (doručené např. AJAX requestem a nebo obyčejným odesláním formuláře - implementaci takového jednoduchého servletu nechám na vás). Jdu rovnou na ChatListener
a ChatGateway
:public void class ChatListener {
public void onMessage(ChatMessage msg);
}
public void class ChatGateway {
private static final ChatGateway instance = new ChatGateway(); //singleton instance
protected List<ChatListener> lsts;
private ChatGateway() {
super();
lsts = new ArrayList<ChatListener>();
}
public void addListener(ChatListener lst) { //přidej posluchače
...
}
public void newMessage(ChatMessage msg) {
List<ChatListener> lsts2 = lsts;
for (ChatListener lst : lsts2) {
lst.onMessage(msg); //každému sděl novou zprávu
}
}
}
V tomto kódu stále ještě není žádné moudro, které by nějak přibližovalo naše řešení a proto pojďme dále k
ChatTracker
:import org.directwebremoting.*;
public class CharTracker implements ChatListener {
protected static final String URL = "..."; //URL stránky, kde se chat odehrává
protected ServerContext srvCtx; //to je celé DWR kouzlo
public void ChatTracker() {
super();
//vytvoříme server context
WebContext webCtx = WebContextFactory.get();
srvCtx = ServerContextFactory.get(webCtx.getServletContext());
//gateway o nás musí vědět
ChatGateway.getInstance().addListener(this);
}
public void onMessage(ChatMessage msg) {
//vytvoříme script, který bude odeslán všem klientům
ScriptBuilder sb = new ScriptBuilder();
sb.appendScript("newMsg(").appendData(msg).appendScript(");");
//pošli script všem klientům, kteří jsou na stránce s definovaným URL
for (ScriptSession ss: serCtx.getScriptSessionsByPage(URL)) {
ss.addScript(sb);
}
}
}
V tomto útržku kódu je velmi podstatný
ServerContext
, což je DWR třída, která pro nás bude prostředníkem pro zjišťování, kteří klienti jsou právě na stránce chatu a má se jim poslat informace o nové zprávě. Každý takový klient je reprezentován instancí třídy ScriptSession
, která je použita k posílání událostí (v našem příkladu k posílání JavaScriptového kódu).Podstatnou drobností je metoda
appendData
třídy ScriptBuilder
, která zakóduje Java objekt do JavaScriptové implementace, přesně v duchu DWR (viz. konfigurace dále).Pro jednoduchost nepoužiji integraci se SpringFrameworkem a použiji přímou konfiguraci DWR pomocí souboru
dwr.xml
, jenž bude umístěn v adresáři WEB-INF
:
<dwr>
<allow>
<create creator="new" javascript="chatTracker" scope="application">
<param name="class" value="ChatTracker"/>
</create>
<convert converter="bean" match="ChatMessage"/>
</allow>
</dwr>
S přihlédnutím k vlastově příspěvku zde není nic neznámého a představíme si definici DWR servletu ve
web.xml
:
<servlet>
<servlet-name>dwr-invoker</servlet-name>
<servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
<init-param>
<param-name>activeReverseAjaxEnabled</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>initApplicationScopeCreatorAtStartup</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
Servlet
DwrServlet
je v jiné package, než uváděl vlasta, což je dáno použitím verze 2.0. Parametr activeReverseAjaxEnabled
povoluje reverse AJAX a initApplicationScopeCreatorAtStartup
zařídí vytvoření instancetřídy
ChatTracker
při startu aplikace, nikolivěk při jejím prvním použití.A co na straně klienta. Asi už je vám jasné, že to bude opět velmi jednoduché:
window.onload = function() {
dwr.engine.setActiveReverseAjax(true);
}
function newMsg(msg) {
if(msg) {
//zde přidej kód pro zobrazení zprávy
}
}
A je to. Je to úžasně jednoduché, to je přesně podle mého gusta. Jediné co mi dělá vrásky na čele, je implementace pouze pro Jetty 6.0, ale už existuje požadavek na vytvoření podpory pro Tomcat 6.0 (DWR-143), tak snad se v budoucnu dočkáme.
Závěr
Ukázal jsem vám, jak pomocí DWR 2.0 a Jetty 6.0 implementovat posílání událostí ze serveru na klienty na příkladu chatu. DWR vám jednoznačně hodně ulehčí v implementování detailů, čímž vám ušetří nejenom čas, ale i nervy. Navíc můžete velmi jednoduše přepínat mezi pollováním, piggybackem a cometem bez zásahu do vašeho kódu, všechny detaily jsou schované uvnitř DWR.
Odkazy
- Ajax, Comet and Jetty (Greg Wilkins, Webtide, 2006)
2 komentáře:
Nemalo by tam byť poling namiesto pooling ?
Mělo by tam být polling ... omlouvám se
Okomentovat