30. prosince 2007

Nová Java: Chceme ji? - podruhé

Blíží se nám konec roku 2007 tak proč nepokračovat v mých představách změny jazyka, tj. takový malý výhled toho co by se v roce 2008 mohlo změnit.

V předchozím příspěvku jsem se rozjel na téma vylepšení programovaní paralelních programů. Dneska se pustím spíš do oblasti vylepšení psaní kódu, tj. změny, které cítím, že zpřehlední kód.

Vylepšení příkazu switch

Jistě všichni víme, že příkaz switch funguje pouze nad typy int a char. Takže žádný long, fload, double, natož např. String (o tomto konkrétním typu hovořili pánové v JavaPosse #151). Kdo se podíval pod pokličku překladu switch příkazu do bytecodu, zjistil, že k tomu slouží dvě instrukce, buď lookupswitch nebo tableswitch. V podstatě jde o to, že tato instrukce na základě hodnoty výrazu ve switch příkazu skočí na správný case. Pokud chceme vytvořit ekvivalent např. pro String, pak použijeme kaskádu if - else if - else if ... .
Proč, ale nepovolit switch i pro jiné výsledné typy výrazu (např. String) a až překladačem vše přeložit stejně jako onu kaskádu ifů?

Odchytávání více typů výjimek najednou při zachování typu výjimky

I v tomto případě jsem se nechal inspirovat povídáním pánů z JavaPosse. No představme si následující metodu:
protected Object invokeMethod(String s) throws IllegalArgumentException, 
IllegalAccessException, InvocationTargetException,
SecurityException, NoSuchMethodException
{
Method m = String.class.getMethod("toString", new Class[0] );
return m.invoke(s, new Object[0]);
}
Nyní chceme tuto metodu zavolat a vyhodit jinou výjimku v případě, že se nepodařilo najít metodu a jinou v případě, že se ji nepodařilo zavolat. Nemáme jinou volbu než:
try {
invokeMethod("A");
} catch (IllegalArgumentException e) {
throw new CantInvokeException(e);
} catch (IllegalAccessException e) {
throw new CantInvokeException(e);
} catch (InvocationTargetException e) {
throw new CantInvokeException(e);
} catch (SecurityException e) {
throw new CantFindMethodException(e);
} catch (NoSuchMethodException e) {
throw new CantFindMethodException(e);
}
A to je vskutnu zvěrstvo. Nebylo by přeci jenom lepší něco následujícího:
try {
invokeMethod("A");
} catch (IllegalArgumentException, IllegalAccessException,
InvocationTargetException: Exception e) {
throw new CantInvokeException(e);
} catch (SecurityException, NoSuchMethodException: Exception e) {
throw new CantFindMethodException(e);
}
Máme možnost specifikovat libovolný počet typů vyjímek, které chceme odchytit jediným catch příkazem, za seznam přidáme dvojtečku a specifikujeme typ proměnné (v našem případě je nejbližším společným předkem výjimek typ Exception, který je zároveň typem proměnné e). Kloním se k variantě, že programátor musí specifikovat typ proměnné, nenechával bych to na překladači.
Co znamená dodatek při zachování typu výjimky z názvu kapitoly? Pokud bychom kód z předchozího úryvku změnili na:
try {
invokeMethod("A");
} catch (IllegalArgumentException, IllegalAccessException,
InvocationTargetException: Exception e) {
//něco
throw e;
} catch (SecurityException, NoSuchMethodException: Exception e) {
//něco jiného
throw e;
}
a uvedli ho v metodě se stejnou klauzulí throws jako má metoda invokeMethod, pak bude vše v pořádku. Překladač se nebude zlobit, že se snažíme vyhodit výjimku typu Exception z metody, která ji nemá uvedenou ve throws, protože proměnná e je typu Exception, ale může nabývat pouze hodnot typů uvedených před dvojtečkou. Samozřejmě musí být final (není tak uvedená, ale je).

Závěr

Obě tyto vychytávky rozhodně nezesložití jazyk, a když tak jenom nepatrně. Ale přinesou bezesporu větší čitelnost řadě algoritmů. To je přínos, který jednoznačně stojí minimálně za zamyšlení.

21. prosince 2007

Nová Java: Chceme ji?

Zamýšlel jsem se nad myšlenkou, změny jazyka Java tak, aby lépe odrážel dnešní potřeby a obsahoval moderní prvky představené především ve skriptovacích jazycích. V poslední době se objevilo pár příspěvků na toto téma, na jedné straně příznivci změn reprezentovaní například JavaPosse #151 a na straně druhé spíše odpůrci, např. Dagi.

Já se spíš stavím za to, aby se jazyk vyvíjel a měnil. Jsem přesvědčen o tom, že požadavky kladené na dnešní "moderní" programovací jazyky jsou diametrálně odlišné od požadavků kladených před oněmi 12 lety, kdy byla Java představena.

Co považuji za nejdůležitejší změnu? Jednoduše použitelná podpora programování multithreadových aplikací. Jistě od roku 1995 máme synchronized, nyní již máme i java.util.concurrent. Do Javy 7 se blíží implementace fork-join algoritmu (pod vedením JSR 166), ale stále si myslím, že je to málo.

Proč chci víc? Programování multithreadových aplikací je stále složitá věc, rozhodně to není pro široké masy programátorů. A dneska je každý nový počítač více-procesorový a abychom využili jejich schopností, pak potřebujeme multithreadovou aplikaci.

Kde bych hledal inspiraci? Co třeba programovací jazyk X10. Ten je dokonce překládán do Javy. Obsahuje řadu konstruktů, které k programování fork-join konstruktů přímo vybízejí (async, atomic, finish, when, atd.).

Myslím, ře prostorů, ve kterých by se Java mohla zlepšovat je jistě nepřeberné množství, a Java bude muset tuto hozenou rukavici zvednout.

7. prosince 2007

Eclipse, Mylyn, IntelliJ IDEA a Teamcity v CZPodcast #19

Ještě jsem poslední (#19) CZ Podcast nedoposlouchal celý, ale když se věci začali točit okolo Eclipse Mylynu, tak bych si troufl provést malé doplnění. Mylyn už nějaký pátek (přesně už to bude přes čtvrt roku) používám a už se dlouho chystám, že se s vámi podělím o své, ve skrze pozitivní, zkušenosti, ale nějak nenacházím tolik potřebnou sílu ...

Takže Teamcity, není Mylyn, ale je to skutečně Continuous Integration server. Mylyn má ve skrze 3 oblasti, kam upírá svou pozornost:

  • práce s bug tracking systémem, plánování požadavků, jejich zobrazování, ukládání kontextu k nim atd.
  • zaměření prostředí na zobrazování pouze relevantních informací v IDE (týkajících se aktuálně zpracovávaného požadavku) = založeno na kontextu, jenž se dá k požadavku uložit - především se to týká filtrování toho, co není k právě realizovanému požadavku relevantní
  • vytváření change setů pro commit do subversion (CVS) na základě kontextu
Co je mi známo, pak IDEA má podporu pro podobnou věc, ale není navázána na bug tracking systém (tj. požadavky si vytváříte ručně) a UI projektu se nijak nemění (nezaměřuje). Tj. sleduje, který soubor se modifikuje a pak vytváří change set pro commit zdrojáků. Podporuje přepínání požadavků a vytvoření správných change setů podobně jako Mylyn.

Sice IDEAu nepoužívám, a nikdy neříkej nikdy .. Třeba mě tento příspěvek nakopne, abych sepsal ten MYLYN.

30. listopadu 2007

Unit testy - ty přece píše kodér, ne?

Tentokrát spíš chci prodiskutovat jedno téma, než vám něco sdělit. Právě jsem dočetl článek Akshay Sharmi o návrhu, který myslí na testovatelnost kódu (Design to Unit Test). Jediná věc, která mě na tomto článku přímo praštila do očí, je skutečnost, že je psán tak, jako že unit testy píše někdo jiný, než ten, kdo testovaný kód psal.

Já jsem vždy přemýšlel o unit testech jako o věci, kterou si kodér ověřuje, že jeho kód funguje tak, jak má. Navíc si kodér musí již dopředu rozmyslet, jak svůj kód otestuje, což bezesporu vede k jeho větší kvalitě. V neposlaení řadě unit testy vnímám také jako "dokumentaci" ke kódu, což v případě, že je píše někdo jiný opět selhává.

Je pravdou, že psaní testů je trochu jiná káva, než psaní kódu. Je nutné se to naučit. Ale není to zase tak složité. V případě integračních a akceptačních testů je již zřejmé, že je může (nebo má?) implementovat někdo jiný než kodér.

Obávám se, že tento přístup není pro mě. Navíc bych si troufl s ním nesouhlasit.

29. listopadu 2007

XML Signature - použít nebo ne?

V naší aplikaci přenášíme poměrně velké XML soubory a přenášíme jich více najednou. Proto jsou zazipované v ZIP archivu. Ovšem potřebujeme nějak zabezpečit, aby nebylo možné měnit obsah archivu (tj. nemožnost přidávat či mazat soubory) a dále aby nešlo měnit jednotlivé XML soubory. Navíc tato komunikace neprobíhá mezi naším softwarem, ale mezi naším a cizím. Jak věc vyřešit?

Použít XML Signature (specifikace) nebo ne? Než jsem se pustil do zkoušení hledal jsem na netu a našel jsem pár mailů v konferencích a blogů, že XML Signature implementace nefunguje dobře na "large documents". Už jsem přemýšlel o vlastním řešení, kde bych hashoval XML soubory jako stream bytů, nebo bych vytvořil nějaký vlastní algoritmus pracující přímo nad SAXem, který používáme jak pro čtení tak i pro zápis dokumentů. Ale nakonec jsem si řekl, že to zkusím přímo s XML Signature a šáhl jsem po implementací, která je součástí JDK 6. Pro XML Signature hovoří skutečnosti, že je to standard (nemusím specifikovat jak se podpis vypočítá) a existuje implementace pro skoro všechny rozšířené jazyky.

Napsal jsem tedy velmi jednoduchou třídu XMLSignatureTest (při jejím psaní jsem převážně vycházel z příkladu v XML Digital Signature API Examples):

public class XMLSignatureTest {

protected static final String KEY_ALIAS = "ALIAS";

public static void main(String[] args) {
try {
long milis = System.currentTimeMillis();
if ("sign".equals(args[0])) {
signing(new File(args[1]), new File(args[2]));
} else {
System.out.println(verify(new File(args[1])) ? "Verified - ok" : "Changed");
}
System.out.println("Done in " + (System.currentTimeMillis() - milis) + " ms.");
} catch (Throwable t) {
t.printStackTrace();
}
}

protected static void signing(File xmlFile, File signatureFile) throws Exception {
//načti vstupní XML dokument
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder builder = dbf.newDocumentBuilder();
Document doc = builder.parse(new FileInputStream(xmlFile));

//otevři keystore a načti z něj privátní klíč k podpisu hashe
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(new FileInputStream("keystore"), "XMLSig".toCharArray());
PrivateKey key = (PrivateKey) keyStore.getKey(KEY_ALIAS, "MEkeypwd".toCharArray());

//nadefinuj context podpisu
DOMSignContext dsc = new DOMSignContext(key, doc.getDocumentElement());
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");

//podepiš celý dokument, na canonicalizaci použij metodu INCLUSIV, jako hash použij SHA1 algoritmus
//podpis bude vložen do podepisovaného dokumentu a výsledný hash bude zakryptovát DSA klíčem
Reference ref = fac.newReference("", fac.newDigestMethod(DigestMethod.SHA1, null),
Collections.singletonList(fac.newTransform(Transform.ENVELOPED, (XMLStructure) null)),
null, null);
SignedInfo si = fac.newSignedInfo(
fac.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE, (XMLStructure) null),
fac.newSignatureMethod(SignatureMethod.DSA_SHA1, null), Collections.singletonList(ref));

//do podpisu jako informaci o použitém klíči vložíme jeho alias
//tj. obě strany se musí domluvit na aliasech a předat si veřejné klíče
KeyInfoFactory kif = fac.getKeyInfoFactory();
KeyName kn = kif.newKeyName(KEY_ALIAS);
KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kn));

//spočítej podpis
XMLSignature signature = fac.newXMLSignature(si, ki);
signature.sign(dsc);

//ulož výsledný dokument do souboru
OutputStream os = new FileOutputStream(signatureFile);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer trans = tf.newTransformer();
trans.transform(new DOMSource(doc), new StreamResult(os));
os.flush();
os.close();
}

protected static boolean verify(File xmlFile) throws Exception {
//načti XML document s vloženým podpisem
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder builder = dbf.newDocumentBuilder();
Document doc = builder.parse(new FileInputStream(xmlFile));

//najdi v dokumentu podpis
NodeList nl = doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
if (nl.getLength() == 0) {
throw new Exception("Cannot find Signature element");
}

//otevři keystore pro načtení veřejného klíče
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(new FileInputStream("keystore"), "XMLSig".toCharArray());

//vytvoř validační kontext (node s podpisem a KeySelector pro získání
//klíče podle identifikace v podpisu - v našem případě je to ALIAS)
DOMValidateContext valContext =
new DOMValidateContext(new KeyNameKeySelector(keyStore), nl.item(0));
XMLSignatureFactory factory = XMLSignatureFactory.getInstance("DOM");
XMLSignature signature = factory.unmarshalXMLSignature(valContext);

//ověř podpis
return signature.validate(valContext);
}

//tato třída slouží k nalezení veřejného klíče použitelného k odkryptování podpisu
private static class KeyNameKeySelector extends KeySelector {
protected KeyStore keyStore;

public KeyNameKeySelector(KeyStore keyStore) {
super();
this.keyStore = keyStore;
}

//tato metoda je volána knihovnou
public KeySelectorResult select(KeyInfo keyInfo, KeySelector.Purpose purpose,
AlgorithmMethod method, XMLCryptoContext context) throws KeySelectorException {

if (keyInfo == null) {
throw new KeySelectorException("Null KeyInfo object!");
}
SignatureMethod sm = (SignatureMethod) method;
List list = keyInfo.getContent();

for (int i = 0; i < list.size(); i++) {
XMLStructure xmlStructure = (XMLStructure) list.get(i);
//my hledáme klíč podle jména (aliasu)
if (xmlStructure instanceof KeyName) {
try {
//natáhnem veřejný klíč z keystoru
Certificate certificate = keyStore.getCertificate(((KeyName) xmlStructure).getName());
PublicKey pk = certificate.getPublicKey();
//ověříme, že v podpisu je stejný algoritmus jako používá klíč
if (algEquals(sm.getAlgorithm(), pk.getAlgorithm())) {
return new SimpleKeySelectorResult(pk);
}
} catch (KeyStoreException kse) {
throw new KeySelectorException(kse);
}
}
}
throw new KeySelectorException("No KeyValue element found!");
}

//pouze porovnává algoritmy
static boolean algEquals(String algURI, String algName) {
if (algName.equalsIgnoreCase("DSA")
&& algURI.equalsIgnoreCase(SignatureMethod.DSA_SHA1)) {
return true;
} else if (algName.equalsIgnoreCase("RSA")
&& algURI.equalsIgnoreCase(SignatureMethod.RSA_SHA1)) {
return true;
} else {
return false;
}
}

//jenom holder pro klíč
private static class SimpleKeySelectorResult implements KeySelectorResult {
private Key pk;

SimpleKeySelectorResult(Key pk) {
this.pk = pk;
}

public Key getKey() {
return pk;
}
}

}

}
Úpravy spočívají pouze v použití key-storu pro získání klíču a nepřenášení veřejného klíče v podpisu, ale pouze jeho aliasu.

A nyní k samotným testům. Zajímal jsem se o dva faktory: rychlost a peměťová náročnost.

Takže nejprve jsem test prováděl na XML dokumentu o velikosti 6,5 kB. Výpočet podpisu i ověření proběhlo i s nejmenším možným heapem, tj. 2M (1M nestačilo ani na nastartování JDKčka) a výpočet podpisu a serializace trvala 0,6 s. Co se verifikace podpisu týče, došel jsem k úplně stejnému číslu.

Následně jsem přitvrdil a použil jsem soubor o velikost 4,1 MB. Minimální heap, nad kterým jsem se dobral výsledku a nedostav pouze OOM exception byl 53MB a výpočet podpisu včetně serializace výsledku trval 10s, ověření podpisu trvalo pouze 4,8s a dokonce potřebovalo minimálně pouze 47MB. Při použití heapu o velikosti 400MB jsem se u podpisu dostal na čas 4,8s a u ověření na 2,8s.

Závěr

V podstatě jsem byl velmi mile překvapen, jak vše hladce a na první pokus proběhlo a spíš jsem hledal skulinu, jak XML Signature použít. Z prvu mi vadilo, že je nutné použít DOM, protože používáme SAX (a použít Transaformer se vstupem SAXSource a s výstupem DOMResult se mi nechtělo). Pak jsem si uvědomil, že bych stejně nemohl výpočet podpisu zařadit přímo do zpracování dokumentu, protož zpracování může trvat i dost dlouho (třeba i desítky minut). Souborů se může zpracovávat více najednou. Pokud by každý, nedejbože, byl rovnou podepisován a vytváření podpisu by si slízlo 50MB heapu, pak bych měl na serveru pamět jenom pro podepisování.

Nakonec jsem se rozhodl, že prvním krokem zpracování bude načtení dokumentu pomocí DOM a ověření podpisu. Následně teprve, pokud bude vše v pořádku, budu dokument načítat pomocí SAX (do budoucna stejně přejdeme na StAX) a dokument budu zpracovávat a zároveň budu generovat výstup (také pomocí SAX) a ten budu serializovat do souboru. Na závěr výstupní soubor načtu pomocí DOM a vygeneruju podpis.

V neposlední řadě, tak trochu pod čarou, zmíním canonicalizaci, která funguje opravdu pěkně, přidávání mezer, nových prázdných řádků, přehazování pořadí atributů neovlivní platnost podpisu.

19. listopadu 2007

JavaScript a Dependency Injection

Všichni jste už asi slyšeli o Spring IoC, Picocontaineru, HaveMindu či Tapestry IoC. Ale což takhle IoC pro JavaScript?

Ano je to možné. Jmenuje se Squirel Ioc. Jedná se o velmi jednoduchou knihovna, která má 4kB a používá jQuery.

Použití je velmi jednoduché (jak už se v mých příspěvcích stalo pravidlem). Máme následující třídy, které budeme propojovat:


function MyModel(){
//obsah třídy MyModel
}

function MyDAO(model){
var _model=model;

this.get = function() {
return _model;
}

this.save = function() {
//uloz
}

this.delete = function() {
//smaz
}
}

function MyService(){
var _dao = null;

this.get = function() {
return _dao.get();
}

this.getDao = function() {
return _dao;
}

this.setDao = function(dao) {
_dao = dao;
}
}

function MyApp(){
var _service=null;
//rikame IoC containeru, ze az vse nastavi, ma zavolat metodu onContextSupport
ContainerSupport.call(this);

this.onContextSupport = function(containerContext){
var model=_service.get();
//udelej cokoliv dalsiho s hodnotou ...
}

this.setService = function(service){
_service = service;
}
}
Z pohledu IoC v Javě máme POJO objekty a teď je potřebuje spolu nějak svázat (ve Springu se k tomu např. používá xml soubor nebo anotace). Při použití Squirrel IoC nám poslouží velmi jednoduchý kód:
var definition={
'model':{type:MyModel},
'dao':{type:MyDAO,args:[{ref:'model'}]},
'service':{type:MyService,props:[{name:'dao',ref:'dao'}]},
'app':{type:MyApp,props:[{name:'containerContext',ref:'containerContext'},{ref:'service'}]}
};


var container = new IContainer();
container.load(definition);
V příkladu je vidět constructor injection (na příkladu definice dao objektu) či setter injection (na příkladu objektu service).

Když to funguje v Javě, proč ne v JavaScriptu. Opět se ukazuje jednoduchost, kterou nám JavaScript jako dynamický jazyk nabízí. A příště až budete potřebovat propojit pár objektů mezi sebou, zamyslete se, zda vám Squirrel Ioc neusnadní práci.

10. listopadu 2007

Nejde říci co je cennější, zda znalosti nebo zkušenosti

Jak je naznačeno v příspěvku Znalosti a zkušenosti, zkušenost je víc než znalost.

Podle mě jde hlavně o rozdíl mezi znalostí a zkušeností. Znalosti jsou laciná věc. Stačí zajít do školy, projít si pár článku na internetu a nebo nedej bože přečíst knihu. Pokud máte funkční paměť nejen v počítači ale i v hlavě, tak znalosti nabudete velice snadno.
A hned jsem si vzpomněl na strýčka z Jirotkova Saturnina, který měl malou chemickou továrnu a nebyl vzdělán v oboru, na všechno si přišel sám. Vše co bylo napsáno v moudrých knihách on složitě vymýšlel. Neměl žádné znalosti, ale nespočetně mnoho zkušeností. Bohužel, vždy když vyráběl nové mýdlo, netušil co se mu v kádích uvaří ...
Jsou to zkušenosti co se počítá. Můžu si přečíst kolik knih chci o tom jak se mají správně dělat softwarové projekty. Dokud to ale nezažiji na vlastní kůži, mají tyto znalosti malou váhu (bojím se že nulovou). Zkušenosti mají také jednu obrovskou výhodu. Málo stárnou. Když mám zažito jak psát software, jaká pravidla dodržovat a čeho se vyvarovat, je mi více méně jedno jakou technologii používám. Detaily si dokáži nastudovat.
Není přeci nutné mít zkušenost s popáleninou od rozpálených kamen, když mám znalost, že to je dost hrozné. Zkušenost dává znalostem rozměrem. Dává jim ten osobní kontakt s nimi. Ale zároveň nám umožňuje tento rozměr v budoucnu dát znalostem přímo, bez zkušenosti. Znalost nás směřuje správným směrem a zaměřuje naše zkoumání a vytváření zkušeností k užitečným cílům.

A teď musím navázat na Vlastůvovu reakci Co se počítá, ve které zase uhodil hřebíček na hlavičku (jak on to dělá?). Nejde o to, jak své dovednosti získáme, či zkušeností nebo znalostí, každý jsme jiný (někomu vyhovuje si přečíst referenční příručku, někdo jiný si věc raději osahá), ale jde o to, jak svých schopností využijeme k plnění cílů, které jsou nám předkládány.

Takže čtěme, vzdělávejme se, nebojme se poučit od druhých, zkušenějších, berme jejich rady vážně, protože jenom tak nezopakujeme chyby, kterých se dopustili, a získáme více drahoceného času. A třeba to dotáhneme ještě dál než oni.

1. listopadu 2007

A je to tady - XQuery Update v pureXML

Kdo si počká, ten se dočká. Od včera je možné si stáhnout IBM DB2 UDB 9.5. A proč tolik povyku. Kromě mnoha další vylepšení, je konečně implementován i draft XQuery Update Facility. Asi nebudu sám, kdo cítil velký nedostatek v nemožnosti updatovat část XML dokumentu uloženého pomocí pureXML.

Nebudu dlouho běhat okolo horké kaše a ukážeme si pár příkladů.

Máme tabulku friend, která má sloupec info. V této tabulce máme jeden záznam:

<friend>
<name>Jiri Mares</name>
<phone type="work">222 222 222</phone>
<phone type="home">333 333 333</phone>
</friend>
A nyní si ukážeme prostou změnu mého čísla do práce (využiji skutečnosti, že mám v tabulce jeden řádek a vypustím klauzili where z SQL update příkazu):
update friend
set info = xmlquery( 'copy $new := $INFO
modify do replace value of $new/friend/phone[@type="work"] with "111 111 111"
return $new');
Což přidat telefon na mobil:
update friend
set info = xmlquery('copy $new := $INFO
modify do insert <phone type="cell">444 444 444</phone>
as last into $new/friend
return $new' );
Nebo můžeme přejmenovat tag name na full-name:
update friend
set info = xmlquery('copy $new := $INFO
modify do rename $new/friend/name as "full-name"
return $new' );
Dále jsem neukázal mazání nodu:
update friend
set info = xmlquery('copy $new := $INFO
modify do delete $new/friend/phone[@type="cell"]
return $new');
Můžeme nahradit více tagů najednou:
update friend
set info = xmlquery(' copy $new := $INFO
modify (
do replace value of $new/friend/phone[@type="home"] with "999 999 999",
do replace value of $new/friend/phone[@type="work"]/@type with "cell")
return $new' );
Začíná se mi XQuery líbit čím dál tím víc, je jednoduché, čitelné, skoro bych řekl sexy :-).

Na závěr nutno říci, že XQuery Update nemusíte používat jenom při SQL updatu, ale i v selectu. Takže pokud nechcete vracet uživateli hodnotu telef. čísel na mobil, pak stačí udělat dotaz:
xquery copy $new := db2-fn:sqlquery("select info from friend")
modify for $j in $new/friend/phone[@type="cell"] return
do replace value of $j with "XXX XXX XXX"
return $new;

A co mě ještě v pureXML zaujalo

Je toho hodně nového, ale ... zajímavá je rychlost, takže je fajn, když se XML dokument může uložit ve stejné stránce jako ostatní sloupce řádku tabulky, protože celý řádek je k dispozici pomocí jediné I/O operace (i když platí omezení na velikost XML dokumentu max. 32k - nebojte, je-li dokument větší, neznamená to chybu, ale pouze se standardně uloží do XDA).

Zajímavá je intergrace XSLT enginu přímo do DB2 (když už je XML uloženo v podobě stromu, přímo si to o tuto integraci říká). Díky nové funkci parameter padá nemožnost předávat parametry do sqlquery funkce. A další ...

26. října 2007

Programujeme nebo lepíme

Přiložím si také trochu do mlýna k diskusi, zda jsou vizuální nástroje dobré nebo špatné. Diskuse se nejprve rozvinula v konferenci konference@java.cz, na to se GUI nástrojů zastal Dagi v příspěvku Potřebujeme silné nástroje a následně Pavel Kolesnikov přidal Potřebujeme domyšlené nástroje. Já se chci trochu vrátit k jádru pudla, zda GUI nástroje ano nebo ne.

Na úvod se přiznám, že nejsem velký přívrženec GUI klikátek, asi už jsem z moc starý školy a jsem moc zakořeněnej v unix shellu. Ale napadlo mě, že dneska už vlastně tolik neprogramujem, spíš skládáme (lepíme) dohromady existující kousky. Však se potichu šušká, že vše už bylo alespoň jednou napsáno.

Ale jaký je rozdíl v použití haldy frameworků, které vše řeší za mě, či GUI klikátka, které ten kód vygeneruje? Ja si myslím, že v podstatě rozdíl není. Cíl je stejný, ale cesta je různá. Mám aplikaci a nemusím moc programovat.

Takže proč vzbuzují GUI klikátka negativnější pocity (alepoň v některých lidech, např. jako já) než frameworky. Za sebe mohu říci že, když jsem je používal já, tak mi hezky zpívaly když mě lapaly. A když jsem je začal používat a následně jsem je potřeboval, pak mě hodily přes palubu, protože složitější věci nešlo realizovat, jak jsem chtěl. A já nerad ustupuju.

A jaký je tedy závěr? I nadále programujeme, ale stále více se uchylujeme k lepení. A zda lepíme pomocí GUI klikátek nebo ne, to si musí každý rozhodnout sám, ale já jsem se rozhodl: "Raději ne, děkuji".

PS: Nejlepším lepidlem posledních let jsou anotace. Abychom se do toho lepidla, ale nezalepili sami.

25. října 2007

Architektura naruby

Právě jsem si přečetl Dagiho příspěvek Do pranice: Spring Web Flow a nebo JBoss Seam a nemohl jsem se nepřidat se stížností na dnešní frameworky. Už toho mám dost, jak se neustále pod rouškou jednoduššího vývoje a já nevím čeho všeho porušují základní pravidla vrstvení aplikace: nižší vrstva nemá vědět, ža nad ní je nějaká vrstva vyšší a využívat pouze služeb svých a nebo služeb vrstev pod ní.

JBoss Seam
je úplně klasickým příkladem této šílenosti, protože vrstvy nižší (v tomto případě např. session beany) jsou oanotovány vším možným co se týká jejich komunikace s JSF (přes Seam) jako vrstvou vyšší. Cestou ven je samozřejmě použití XML konfigurace, ale ta se v souvislosti se Seamem skoro neprezentuje.

Z podobného důvodu používám s Hibernate vždy XML konfiguraci. Prostě nechci aby můj model byl zaneřáděn anotacemi, které ke své funkci vůbec nepotřebuje. Mapování uložení do DB je přeci věc DAO vrstvy a model se o něj rozhodně nemá starat.

Ano, je to složitější, pracnější, ale rozhodně je to flexibilnější. Řada čtenářů my možná namítne, že na to není čas, že je jim tato flexibilita na nic atd. Je to každého věc, ale já mám flexibilitu rád a nevzdám se jí. Navíc mám rád pokud je kód přehledný, hezky strukturovaný a provázaný jenom jedním směrem, shora dolů a ne i naopak.

22. října 2007

Jednoduše na CSS Sprite

O CSS Sprite už jsem psal v příspěvku Zaměřte se na fronted, takže jenom z rychlíku. Jedná se o více obrázků rozložených v jednom velkém, které jsou následně pomocí CSS backgroup zobrazovány. Získává se tím větší výkon stránky, protože pro její zobrazení není nutné natahovat 30 malých obrázků (=30 requestů), ale pouze jeden větší.

A já vám nyní předkládám odkaz na stránku, kde si takový CSS Sprite můžete vytvořit. Zazipujte své malé obrázky, uploadněte je a získáte zpět jeden velký. Proč se s tím pachtit v GIMPu.

19. října 2007

Jak na vlastní design File Input položek formulářů

Každý již jistě někdy psal stránku, která umžňovala upload nějakého souboru. To je problém celkem jednoduše řešitelný, ovšem problém nastává, pokud máte nějaký design tlačítek na stránce a chcete, aby i tlačítko pro výběr souboru mělo jednotný design. A zde se už naráží.

Ovšem i toto je věc řešitelná (ve světě počítačů snad neexistují neřešitelné problémy). Celý postup názorně popisuje Shaun Inman ve svém blogu. V principu jde o kombinaci CSS, DOM a JavaScriptu.


Funguje to tak, že se input obalí wrapper-tagem (např. label), jemuž se nastaví pozadí na námi požadovaný vzhled. Následně se nastaví neprůhlednost (opacity) tagu input na 0, čímž se zneviditelní, ovšem stále je klikatelný. No a zavěrečný krůček nám pomůže udělat JavaScript, kterým zařídíme, že tlačítko pro výběr souboru bude vždy umístěno pod ukazatelem myši, pokud je uvnitř našeho wrapper-tagu.

A teď v jakých prohlížečích to funguje? Překvapivě IE 5.5+, Firefox 1.5+, Safari 2+. Uspokojivě je použitelné v IE 5.01 a Opeře.

18. října 2007

Jak ovládat Eclipse pomocí prohlížeče

O Eclipse RAP se už blogovalo (např. dagi), ale o integraci Eclipse s prohlížečem zatím ne. Celé se to jmenoje Web Broser-Based Integration with Eclipse IDE, a jde o skutečné ovládání Eclipse pomocí prohlíče. Nainstalujete speciální plugin včetně Jetty serveru a to je celé. V nastavení eclipse spustíte server, do prohlížeče (pozor celé to funguje pouze ve Firefoxu) zadáte adresu http://localhost:9008/ a zobrazí se vám váš Eclipse.

A takhle vypadá originál:

Celé to funguje velmi jednoduše, skoro se mi chce napsat trapně jednoduše, UI Eclipsu se překládá do XUL Firefoxu, který jej pak zobrazuje.


Zatím je to jenom preview, ale možnosti jsou opravdu velmi široké, od přístupu ke své Eclipse instalci pomocí prohížeče (či k centrální instalaci s definovanými pluginy a projekty), při podpoře více uživatelů (zatím chybí) dokonce může více uživatelů sdílet jednu instalaci Eclipse až po vývoj webových aplikací pomocí Eclipse RCP.

12. října 2007

Vývoj aplikace za běhu

Dostala se mi pod nos opravdu zajímavá věcička, která si říká JavaRebel. O co se jedná? Jedná se o udělátko, které za běhu aplikace dokáže reloadovat třídy. A pozor, nepoužívá k tomu class loader (tak problém řeší Howard Lewis Ship pro Tapestry 5) ani hot swap (jenž je v javě od verze 1.4).

Ale jak to tedy dělají? Jak sami říkají, pomocí změny bytecodu a trochu kouzel. Každopádně JavaRebel podporuje všechny změny tříd až na změnu v dědičnosti (tj. extends a implements). Dá se použít jak při vývoji webových tak klientských aplikací.

Po Terracotte je to další technologie, která používá manipulace s bytecodem k docílení až zázračných věcí.

Jediné mínus je, že se jedná o komerční produkt, a stojí 100$ na vývojáře. Ovšem to je investice, která se vrátí snad už za týden.

Odkazy


8. října 2007

Kam se ztrácí zkušení programátoři

Přečetl jsem si velmi zajímavý článek API: Design Matters od Michi Henninga. Je velmi poučný a vhodně doplňuje prezentaci Jaroslava Tulacha Jak psát API, které přežije nástrahy času (slidy, video). Takže rozhodně si ho přečtěte, protože API navrhuje každý z nás.

Ovšem co mě skutečně praštilo do očí je jedna z posledních kapitol, která se designem API zaobírá spíše okrajově. Hovoří o tom, že zkušení programátoři (a v podstatě i designéři či architekti) přestávají existovat. Jak je to možné?

Upřímně kolik znáte programátorů, kterým je přes 40? Já jsem zatím poznal tři (a zkušenost je jednoznačně pozitivní), takže mladší jednoznačně převládají. Proč? Protože je sociální tlak, aby programátor skončil s programováním včas a přeorientoval se na designéra nebo architekta, v nejhorším případě na project managera, protože přeci nemůže na ty mladé kluky, co se nové technologie učí na škole, stačit.

Ovšem co umí mladý kluk, když opouští univerzitu. Umí minimálně 5 programovacích jazyků (což by jistě s knihou a chvilkou času zvládl i bez učitele), umí řadu děsně zajímavých algoritmů, které většinou nikdy nepoužije a když tak velmi vyjímečně. Ale to co by měl používat denně: objektový design, psaní přehledného kódu, test driven development, code coverage, continuous integration, nedej bože jiné metodiky vývoje než vodopád (nejlépe scrum). To nezná.

A kde je problém designérů a architektů? Jsou drazí a je jich málo. Proto nemohou na projektu zůstávat po celý jeho životní cyklus a tím se odtrhávájí od reality, protože netuší jaké dopady mají jejich rozhodnutí. Chybí jim zpětná vazba.

Takže kdeže jsou ti zkušení? Neexistují ...

4. října 2007

Velmi jednoduchá validace HTML formulářů JavaScriptem

Dneska mi padla do oka velmi jednoduchá knihovnička na validování HTMl formulářů pomocí JavaScriptu - JSValidate. V podstatě bych řekl, že se mi víc líbí nápad, jak jsou položky formuláře označovány na co mají být validovány, než samotná knihovna, protože obsahuje validaci pouze JavaScriptem, tj. na klientu, a to je pro mě málo.
A co je to tedy za nápad? Prostě a jednoduše položky formuláře, tj. input tagy označíte CSS třídou podle toho, jak jej chcete validovat a přidáte následující 3 řádky na začátek stránky:

<script type="text/javascript" language="javascript" src="scriptaculous/lib/prototype.js"></script>
<script type="text/javascript" language="javascript" src="scriptaculous/src/scriptaculous.js"></script>
<script type="text/javascript" language="javascript" src="jsvalidate.js"></script>
A jste hotovy.

Příklady

Jednoduchá položka, jejíž hodnota je povinná:
<input type="text" name="name" class="jsrequired" />

Nebo položka pro číslo:
<input type="text" name="number" class="jsvalidate_number" />
A nebo položka na email, který je nutné zadat:
<input type="text" name="email" class="jsrequired jsvalidate_email" />

Jaké jsou dostupné třídy

  • jsrequired - musí být vyplněna
  • jsvalidate_number - číslo
  • jsvalidate_digits - pouze číslice
  • jsvalidate_alpha - pouze písmena
  • jsvalidate_alphanum - písmena, číslice a podtržítko
  • jsvalidate_email - email
  • jsvalidate_uscanzip - ZIP code USA nebo Kanady
  • jsvalidate_usstate - 2 znakový kód státu v USA
  • jsvalidate_usphone - telefonní číslo v USA
  • jsvalidate_creditcard - číslo kreditní karty
  • jsvalidate_ssn - Social Security Number - číslo sociálního pojištění (USA)
  • select-notfirst - používá se u selectů, kde zajistí, že první položka ze seznamu nemůže být vybrána

Závěr

Myslím, že existuje problém, kde se tato knihovná dá nasadit. Asi to nebude do žádné větší aplikace, kde bude na validační framework kladen podstatně větší nárok, především mám na mysli prováznost s validací na serveru.
Co se mi jeví jako hodně zajímavé je využití CSS tříd pro identifikaci, to je velmi jednoduché a elegantní.

Odkazy

17. září 2007

Zaměřte se na frontend

Potřebujete zvýšit výkon vašich stránek z pohledu koncového uživatele? Zaměřte se na frontend, ne na backend. To je obsah prezentace Steva Sounderse z Yahoo. A proč? Při analýze výkonu Yahoo stránek se ukázalo, že pouze 12% z doby potřebné pro zobrazení stránek je čas strávený generováním obsahu na serveru. Zbytek je přenos dat a práce klienta. Tento průzkum byl potvrzen na výkonu 9 z 10 TOP10 amerických webů, kdy více jak 20% zlepšení v rychlosti zobrazení stránek po natažení všeho cachevatelného do cache zaznamenal pouze Google.

A jaké jsou tedy rady:

  1. čím méně HTTP requestů, tím lépe - spojujte jvascriptové a CSS soubory, používejte CSS Sprites
  2. používejte CDN (Content Delivery Network) - distribuujte nejprve statický a pak dynamický obsah
  3. používejte Expires hlavičky - umožní cachování obsahu
  4. gzipujte posílaný obsah - 90% klientů podporuje kompresi obsahu pomocí gzipu, využijte jej
  5. CSS stylesheety dejte na začátek stránky - IE zobrazí stránku, až když má nateženy všechny stylesheety, proto je nutné je začít stahovat co nejdřív
  6. javascript naopak dejte na konec - máte-li javascript inline v dokumentu, pak blokuje renderování něčeho viditelného, je-li jako externí soubor, pak blokuje (především v IE) stahování něčeho vizuálního
  7. nepoužívejte CSS výrazy - něco jako width: expression (document.body.clientWidth < 600 ? "600px" : "auto"); příšerně zpomaluje stránku, protože takový výraz se vyhodnocuje pořád (při pohybu myši, při stisku klávesy, ...)
  8. CSS a javascript mají být v externích souborech - sice přidávají HTTP request, ale mohou být cachovány
  9. minimalizovat počet DNS lookupů
  10. Minify Javascript (případně obfuscate) - zmenší objem javascriptu, ale nemění funkci
  11. vyvarujte se redirectů - zablokují renderování stránky a oddalují jej
  12. odstraňte duplicity - ve scriptech, stylesheetech, ...
  13. snažte se používat ETags
  14. udělejte AJAX cachovatelný - i requesty prováděné pomocí AJAX by měly být cachovány
Musím říci, že tato prezentace byla pro mě překvapující, protože bych čekal, že za zvýšením výkonu stránek bude nějaká magická serverová technologie nebo vychytávka. A ejhle ono ne. Je pravda, že na přizpůsobení stránek rychlejšímu zpracování na klientu se moc velká pozornost nevěnuje, zde je taková malá kapesní kuchařka.

Tento příspěvek je spíše lákáním na shlédnutí ani ne 40 min prezentace, jistě je to zajímavé poslouchání.

PS: v prezentaci se hovoří o plugin do Firefoxu YSLOW, který analyzuje stránky a říka proč jsou pomalé, rozhodně stojí za vyzkoušení, jistě se dozvíte zajímavé věci.

Code review je jistě užitečná věc

Když jsem si přečetl dagiho příspěvek o code review vyskočil jsem jak čertík z krabičky. Než jsem došel domu, málem jsem na své rozhodnotí reagovat zapomněl, ale už jsem si zase vzpomněl.

Jsem zvyklý code review používat a považuji jej za skvělou věc. Ovšem nejprve co to je code review? Jak jej vnímám já, není to kontrola, že kód splňuje nějaký coding standard, není to náhrada testů, tj. kontrola správnosti (ve smyslu fungování) a ani to není kontrola na neexistenci známých chybových paternů. Jsem tedy plně v souladu s dagim, že code review nemá plnit úkoly, které může plnit automat (počítač) pomocí PMD, Checkstyle, FindBugs apod.

Co tedy má plnit code review? Řekl bych, s trochou nadsázky, že má plnit to co pair-programming, akorát, že prográmátoři se baví už nad hotovým kódem. Tj. ten co dělá code review, sleduje kód, jak je napsán, zda mu rozumí (podstatná věc, protože pak je pravděpodobé, že to pochopí i někdo další), zda je kód rozumně strukturován a pod. Samozřejmě se jeden učí od druhého, protože buď se koukám na kód, který mě něčím inspiruje a nebo se mi v něm něco nelíbí, požaduju změnu a inspiruji autora.

U nás v týmu provádíme code review po jednotlivých požadavcích, které programátoři plní. Každý požadavek má přidělen implementátora a reviewera. Teprve pokud je review hotové, všechny připomínky vyřešeny, pak se požadavek zavírá jako hotový.

Tento postup se nám v praxi ověřil, jako kontrolní mechanismus, jako princip vzdělávání, jako postup pro zlepšování kvality kódu. Takže závěr, code review ano, ale ne na automatizovatelné úkony.

15. září 2007

OpenAjax Hub = javascriptový messaging v 5kB

Dneska si ještě více rozšíříme znalosti na poli AJAX technologií. Z blízka se podíváme na OpenAjax Hub z pod pokličky OpenAjax Alliance. V podstatě jde o messaging pro JavaScript napsaný v JavaScriptě, nebo o jednoduchý, ale mocný, publish/subscribe hub.

Pokud děláte AJAX aplikace, které jsou složené z komponent, jenž si mezi sebou posílají události, pak čtěte dále. Pokud použijeme comet, pak komponentou může být i server (jak na to je popsáno v DWR a Jetty = výkoný AJAX naruby). Obzvláště výhodné je použití OpenAjax Hubu v případech, kdy jedna komponenta generuje události, které přijímá více komponent. Navíc hub sebou nese nezávislost jednotlivých komponent, tj. komponenty komunikují pouze s hubem a ne mezi sebou, tj. čím více komponent, tím větší zjednodušení.

My jsme měli např. problém v naší aplikaci, která sleduje pohyb autobusů (autobus se ohlašuje pomocí GPRS a posílá svou polohu získanou z GPS). Chtěli jsme polohu zobrazit nejenom v mapě, ale také pomocí tabulky, která by zobrazovala další informace (např. zda jede na čas, jak je daleko na své trase).

Jak Hub použít


Pokud se proklikáte na sourceforge stránky projektu a stáhnete si OpenAjax Hub možná budete překvapeni, protože soubor OpenAjax.js, který obsahuje implementaci Hubu má plných 5744 B. Ano není to překlep, méně než 6 kB.

Nejprve začnu serverovou částí, kde použijeme Reverse AJAX. Oproti příspěvku s chatem, kde jsme volali funkci newMsg(msg) budeme volat funkci OpenAjax.hub.publish("bus.location", busLocation) (použijeme k tomu objekt ScriptBuffer jako minule). Jako první parametr do funkce publish předáme jméno události a druhým parametrem je samotná událost (využijeme DWR, které převede java objekt do javasriptového).

Na klientu musíme zapnout reverse Ajax v DWR. Dále se zaregistrujeme k odběru událostí, což provedeme kódem:

var subscription = OpenAjax.hub.subscribe("bus.location", showBusLocationInMap);

Prvním parametrem je jméno události. Pokud se chceme přihlásit k odběru všech událostí týkajících se autobusů, pak použijeme wildcard a zapíšeme jméno události bus.*. To znamená i bus.state nebo bus.message. Pokud se chceme přihlásit i k odběru bus.data.check pak použijeme bus.**.
Druhým parametrem je callbak funkce, kterou si popíšeme dále. Dalšími parametry, které v příkladu nevyužíváme jsou:
  • scope - objekt, který bude this při volání callbacku (je-li null, pak je použit objekt window)
  • subscriberData - objekt, který je předán callback funkci jako parametr
  • filter - funkce, která vrací true/false v případě, že akceptujeme/odmítáme předanou událost
Návratovou hodnotou funkce subscribe je objekt, který se použije jako parametr při volání unsubscribe.

Callback bude mít následující podobu:

function showBusLocationInMap(name, event, subscriberData) {
//změň polohu autobusu na mapě - data jsou v objektu event
}

Nemůžu se stále ubránit dojmu, že je to geniální. Skvělý nápad, skvělá implementace, velmi jednoduché použití.

Závěr


Dneska jsem vám ukázal co to je OpenAjax Hub. Jak jej použít pro distribuci událostí na principu publish / subscribe. Ve spolupráci s DWR a jeho reverse AJAX implementací jsme navíc velmi jednoduše vytvářeli události na serveru.

To co je ESB pro distribuované systémy, to je OpenAjax Hub pro AJAX aplikace. Pokud tvoříte portálová řešení, mashupy, či složitější aplikace, pak se vám zjednodušení vnitřní komunikace, které vám nabízí OpenAjax Hub bude určitě hodit.

Odkazy


14. září 2007

DWR a Jetty = výkoný AJAX naruby

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)
Ovšem přeci jenom jednu nevýhodu tento přístup má. Pokud bychom nevytvářeli aplikace "inteligentně", pak každé spojení klienta se serverem, které se používá k posílání dat ze serveru na klienta, nám použije jedno vlákno na serveru, což bude řešení, které nebude moc škálovatelné.

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 podpory java.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í instance
tří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

10. září 2007

Něco úvodem

Nejprve vám napíšu něco o sobě. Primárně jsem programátor. Takže se na těchto stránkách budete potkávat s různými věcmi týkajícími se programování, tj. od serveru po klienta, od Javy po SQL, od IDE po profiler atd. A abych nezapomněl, asi se tu budete potkávat i s detaily o projektu cobertura, protože to je už i moje dítě.

A proč mít vlastní blog? Vede mě k tomu pocit, že se chci podělit o svoje zkušenosti, které věřím nejsou malé a budou čím dál větší. Snad budu mít stále i chuť sepisovat své zážitky, aby zde bylo co číst.