27. října 2008

jOpenSpace 2008 - Java vs. dynamické jazyky - více návratových hodnot v Javě (aktualizováno)

Opravil jsem špatně uvedenou ukázku Tuple3.

V záznamu z jOpenSpace na téma Java vs. dynamické jazyky jsem zaznamenal postesknutí: jediné co mi v Javě chybí je možnost, aby metoda měla více návratových hodnot. Ovšem je nutné si přiznat, že to je možné již nyní. Správný název pro objekt, který je de facto kolekcí s tou vlastností, že každý prvek je jiného typu.

Simulovat tuto funkcionalitu pomocí List<Object>, ale to není cesta správným směrem. Dále jsem našel knihovnu javatuple, ta se mi nelíbila, protože místo universálního slova tuple se třídy jmenují Tuple, Triple, ... Nakonec jsme si implementaci napsali sami, a nelitujeme.

Takže třída Tuple2 vypydá:

public class Tuple2<A, B> implements Serializable {
   protected A v1;
   protected B v2;

   private static final long serialVersionUID = -4987109478796050933L;

   public Tuple2(A v1, B v2) {
      super();
      setV1(v1);
      setV2(v2);
   }

   public A getV1() {
      return v1;
   }

   public void setV1(A v1) {
      this.v1 = v1;
   }

   public B getV2() {
      return v2;
   }

   public void setV2(B v2) {
      this.v2 = v2;
   }

   @Override
   public boolean equals(Object obj) {
      if (this == objreturn true;
      if ((obj == null|| !(obj instanceof Tuple2)) return false;
      Tuple2<?, ?> other = (Tuple2<?, ?>obj;
      return ObjectUtils.equals(getV1(), other.getV1()) && ObjectUtils.equals(getV2(), other.getV2());
   }

   @Override
   public int hashCode() {
      int hashCode = 0;
      if (getV1() != nullhashCode += getV1().hashCode();
      hashCode *= 31;
      if (getV2() != nullhashCode += getV2().hashCode();
      return hashCode;
   }

}
Dále následuje Tuple3:

public class Tuple3<A, B, C> extends Tuple2<A, B> {
   protected C v3;

   private static final long serialVersionUID = -4987109478796050933L;

   public Tuple3(A v1, B v2, C v3) {
      super(v1, v2);
      setV3(v3);
   }

   public C getV3() {
      return v3;
   }

   public void setV3(C v3) {
      this.v3 = v3;
   }

   @Override
   public boolean equals(Object obj) {
      if (this == objreturn true;
      if ((obj == null|| !(obj instanceof Tuple3)) return false;
      Tuple3<?, ?, ?> other = (Tuple3<?, ?, ?>obj;
      return super.equals(obj&& ObjectUtils.equals(getV3(), other.getV3());
   }

   @Override
   public int hashCode() {
      return super.hashCode() 31 ((getV3() == null: getV3().hashCode());
   }

}
Kód pro Tuple4 je již jednoduše vytvořitelný. Zbývá vytvořit implementace až kam potřebujete (my jsme se zastavili na čísle 7). Na závěr jsem vytvořil factory objekt, aby bylo vytváření pohodlnější:

public class Tuple {

   public static <A, B> Tuple2<A, B> newTuple(A a, B b) {
      return new Tuple2<A, B>(a, b);
   }

   public static <A, B, C> Tuple3<A, B, C> newTuple(A a, B b, C c) {
      return new Tuple3<A, B, C>(a, b, c);
   }

   ...

}


Když na tuto implementaci koukám, není mi jasné proč standardní knihovna neobsahuje něco podobného. I bez podpory přímo v jazyce je to řešení použitelné.

Google Developer Day 2008

V pátek 24. října jsem se zúčastnil akce pořádané společností Google s názvem Google Developer Day v Hotelu Clarion ve Vysočanech. Protože mi to klasicky trvá déle než něco napíšu, pak vás mohu odkázat na Postřehy Martina Hassmana a Pavla Cvrčka, kteří byli rychlejší. Také jsem k celé akci přistupoval s despektem a vzhledem k naléhavosti úkolů z práce jsem se málem nezúčastnil. Ale nakonec musím říci, že mé pocity jsou podstatně pozitivnější než např. z poslední návštevy Sun Tech Days.

Úvodní key note se nesla v duchu toho, že pokud bude žít web bude žít i Google. A dále uvedla do kontextu všechna témata, kterých se celý den měl týkat, tj. Chrome, AppEngine, GWT, Apps (Search, Maps, Data API, ...), OpenSocial a Android. Pak již paralelně probíhaly jednotlivé přednášky ve 4 sálech, v pátem se konaly workshopy a v šestém byla relax zóna. Můj vkus byl malinko jiný než ostatních, tak alespoň přispěji k zacelení mezer v popisu přednášek.

Jako první jsem zvolil úvod do AppEnginu - Dive into Google App Engine (zvažoval jsem JavaScriptový engine V8, ale nakonec jsem se rozhodl pro AppEngine, který spíš využiju). Přednáška byla nakonec tím nejzajímavějším co jsem viděl. Vždy mě trápilo, když bych chtěl psát nějakou aplikaci pro web, kde ji pustím. AppEngine je odpovědí na tyto starosti. Programuje se v něm velmi jednoduše, Python není složitý jazyk, navíc se chystá další jazyk (pokličku se pozvednout nepodařilo, takže není jasné o jaký jazyk se jedná). Kdo píšete aplikace pro web, hledáte hosting a nechcete psát v PHP, mrkněte na něj.

Následovala přednáška o výkonu aplikací napsaných v GWT potažmo všech webových aplikací - Measure in millseconds: Performance tips for GWT (and AJAX in general). GWT nepoužívám takže jsem si jej trochu přiblížil (v podstatě ještě z doby programování v C++ mám odpor k jakémukoliv generovanému kódu a proto jde i nějak GWT mimo mě). Ovšem mile mě překvapilo, jak je to sofistikovaný systém. Co se dalších tipů týče, nic nového jsem se nedozvěděl, snažit se mít co nejméně CSS a JavaScript souborů, mít je co nejmenší a používat CSS Sprite, to jsou dnes již design pattern webového vývojáře.

Pak jsem si řekl, že to risknu a dozvím se něco o social networks. Zavítal jsem na přednášku Best practice for OpenSocial development. Pro mě nejslabší zážitek. Měl jsem pocit, že jsem buď moc starej a nebo z jiné planety, protože jsem nějak nechápal, kdo používá takové věci, zkrátka nejsem nějak in.

Zasklil jsem i Android (o tom se dá načíst ledacost) a zavítal jsem na jedinou českou přednášku Large-scale computing, Google-style: MapReduce, BigTable, Hadoop, HDFS and others přednášenou Peter Kukolem. Na MapReduce se moc nedostalo. Ale bylo to hodně zajímavé, protože se řešilo co to znamená computing v rozměrech Googlu, co vše se stává problémem při počítání v takovém měřítku.

Na závěr jsem shlédl Integrating 'Google' into your applications: Google Data APIs & AJAX APIs, týkající se popisu rozhraní k aplikacím Googlu. Zde jsem se dozvěděl o různých rozhraních ke Google Search (JavaScriptové či REST) či o Google Data API, které se používá pro přístup (čtení i zápis) k datům v kalendáři či Google Apps.

Zbaběle jsem utekl před večírkem a pelášil domů za rodinkou. Dojmy pozitivní, jsem moc rád že jsem se mohl zúčastnit, příště rád půjdu zas. PS. Moc se mi líbil přístup k lidem s notebookem, všude halda prodlužovaček a přístup na net, prostě super. Já dávám jedničku s hvězdičkou.

jOpenSpace 2008 - Spring - otevřenost novinkám

Už jsem se bál, že zbytek záznamů z nultého ročníku jOpenSpace neexistují a hned jsem dostal odpověď v podobě dalších pěti záznamů. Šestým v pořadí je záznam od Dagiho Spring adoption in large enterprise project. Spring používáme a převážně jako IoC container, tak mě toto téma zajímalo. Bohužel kvalita zvuku je hodně špatná, takže jsem spíš neslyšel než slyšel.

Přesto jsem zaslechl jednu poznámku, která mě vybudila zamyslet se na téma: Kde se v lidech bere touha poznávat a používat nové vs. držet se v zajetých kolejích? Řadil bych se spíš k těm zvídavým, které nové věci zajímají a přemýšlejí o jejich aplikaci.

Je to dáno tím, že mě programování zajímá? Že to je i můj koníček. Nebo je to tím, že chci něčeho ve své profesi dosáhnout a proto se snažím? Nebo je to prostě tím, že věci většinou dělám pořádně a nesnažím se něco odfláknout, jenom proto abych už to měl za sebou? Asi je nutná kombinace všech zmíněných důvodů, aby programátor měl chuť se sebevzdělávat a tím byl otevřený novým věcem.

Vždyť všechny lidské činnosti podléhají zubu času, stále se hledají lepší a dokonalejší řešení čehokoliv, jenom v oblasti IT je to všechno trochu markantnější a rychlejší. Ovšem pozor, i s prosazováním nových věcí to není tak jednoduché. Je nutné se na ně vždy podívat i skepticky, protože hrozí 2 věci. První je over-engineering projektu, kdy na řešení problému používáme příliš složité technologie. A druhým problémem je vklouznutí do slepé uličky, protože ne vše co se v době svého vzniku jeví jako dobré se dobrým ukáže i po čase používání.

Ve svém důsledku se obávám, že i ty skeptiky, kteří neradi posouvají své znalosti někam dál, potřebujeme. Není přeci nic podnětnějšího než přesvědčování takového člověka, protože je nutné vymýšlet argumenty a při této činnosti se kolikrát ukáže, že jsme měli na očích růžové brýle.

Takže závěrem: doufám, že budeme mít vždy šťastnou ruku ve volbě novinek a že se budu potkávat spíš s těmi nadšenci pro nové věci, které budeme moci probírat a řešit, zda jsou užitečné.

21. října 2008

jOpenSpace 2008 - Metodiky vývoje - jak děláme review kódu

Dneska vám předkládám poslední reakci na první dávku audií z jOpenSpace, po ORM a dokumentaci mě oslovila diskuse na téma metodiky vývoje. Poslední 2 zaznamenaná témata mě k nějakým poznámkám nevyburcovala.

Co se metodiky vývoje v podstatě mě velmi mile překvapilo, že se již naplno prosazují iterativní a agilní způsoby vývoje, které začínají odsouvat nevhodný vodopád. To je velmi potěšující. Je až nepochopitelně překvapující, že se vodopád udržel tak dlouho (mimo jiné podle knihy Craiga Larmana Agile and Iterative Development: A Manager's Guide je zjištění jeho nefunkčnosti hodně staré).

Poslední věc z diskuse je taky všude stejná. Zapojit zákazníka do vývoje je velmi težké, ale pokud se to podaří, je to super věc. My se především staráme o jednu už né-malou aplikaci, kterou poskytujeme jako službu. Je-li zákazník interní (vymyslíme si nějakou funkcionalitu, či vyvíjíme něco dovnitř) pak je to super, je-li zákazník externí, pak je odezva většinou pomalá.

Co ovšem vzbudilo mou chuť napsat tento příspěvek, je poměrně složitý způsob reviewováví kódu, které bylo nastíněno. Protože s naším systémem jsem poměrně hodně spokojen a mám pocit, že je na úrovni, rád se s ním s vámi podělím. Review jsem od začátku zamýšlel tak, aby každý kód, který vytvoříme byl reviewován. Z počátku jsme kontrolovali příchozí změny z CVSka. Pak nás začlo být v teamu víc než 2 a tento postup přestal fungovat.

Zhruba v tu dobu jsme nasadili systém JIRA pro evidenci požadavků. Tím se nám nabídla možnost evidovat jakoukoliv práci na projektu jako požadavek, který definuje, kdo jej bude implementovat a zároveň kdo jej bude reviewovat. Požadavek nemůže být zavřen, pokud nebylo provedeno review. Jednoznačnou výhodou je přehled toho, co se na projektu děje. Dále review provádíme průběžně a nikoliv až na konci iterace (přecijenom dělání review není moc populární a zajímavé), navíc je review skutečně provedeno na každý změněný řádek a to považuji za podstatné (víc očí víc vidí a rozumí-li kódu dva mozky, pak jim pravděpodobně bude rozumět i třetí).

Jako vývojový nástroj používáme Eclipse a proto jsem hledal podporu review kódu integrovatelnou do Eclipse. Jediné co jsem našel byl Jupiter (koukám, že se na něm usilovně pracuje, tak třeba se zlepší). Dlouho jsme jej používali a zjistili jsme, že má řadu nevýhod a proto jsem hledal náhradu. Koukal jsem na Crucible, ale nelíbilo se mi, že by evidence review byla mimo již existující infrastrukturu (podobně jako v případě Jupiteru).

Jako zatím top volba se ukazuje integrovat systém JIRA s naším subversion serverem (pomocí subversion pluginu) tak, aby u každého požadavku zobrazoval change sety, které se jej týkají (k tomu stačí, aby komentář commitu do subversion obsahoval kód požadavku). Následně jsme potřebovali nástroj na zobrazení obsahu subversion a volba padla na Fisheye, protože je sofistikovanější než ViewVC.

A jak samotné review probíhá? Je-li požadavek vyřešen, pak je nutno provést review. Podívám se na požadavek a vidím jaké soubory, byly v souvislosti s jeho řešením commitovány. Přímo ze systému JIRA je možno se prokliknout do Fisheye, kde potřebuji další klik na zobrazení změn souboru s požadavkem souvisejících. Je-li vše v pořádku, pak stačí požadavek označit jako reviewovaný a pak už jej může vedoucí projektu zavřít.

Není-li vše v pořádku, pak přijde na řadu diskuse. Tu nijak nedokomuntujeme a je věcí programátorů, jak ji zrealizují. Pokud se ukáže, že je nutné provést změnu kódu (překvapivě se to děje docela často), pak je v systému JIRA vytvořen další požadavek na změnu (nepřehledný kód, nějaká chyba v kódu, chybějící test atd.). Tento požadavek je normálně řešen a je i reviewován jako každý jiný. Commit do subversion obsahuje kódy obou požadavků, aby byl v systému JIRA spojen s oběma (jak s původním, tak s review).

A jaký má systém výhody a nevýhody? Řadu už jsem jich zmínil, ovšem další výhoda tkví v přesné evidenci toho co se děje, v jakém stavu je který požadavek a co se v souvislosti s jeho plněním měnilo a jak. To je neocenitelné. A nevýhody? Pokud je požadavek hodně rozsáhlý, pak je složitější se v souborech vyznat. Je-li to umocněno více change sety (tj. více commity), je to skutečně náročné.

A uplným závěrem motivační větu: Review kódu skutečně zvyšuje jeho kvalitu a napomáhá dřívějšímu odhalení chyb. Navíc se programátoři učí číst kód druhých, orientovat se v něm, komunikovat o něm a lépe poznávají systém, který programují.

14. října 2008

Spring bean z Java enumu

Dneska jsem potřeboval vytvořit ve Spring IoC Java enum, ale jak na to. Než jsem začal vytvářet nějakou factory, řekl jsem si, že to už musí být vyřešené. Google pomohl a našel jsem blog post Creating a Spring Bean from a Java 5 Enum.

Ve stručnosti se věc dá shrnout do dvou ukázek, první jest pro Spring 1.2.2 a menší:

<bean id="latte" class="cz.xxx.PersonType" factory-method="valueOf">
<constructor-arg>
<value>ADULT</value>
</constructor-arg>
</bean>
Kde ADULT je jméno konkrétního enumu. Domnívám se (nezkoušel jsem), že tuto variantu je nutné použít i ve Springu 1.2.2 a vyšším, pokud se odkazujete na enum v okamžiku, kdy není jasné, že výsledkem má být enum (např. položky listu, mapy).

Používáte-li Spring 1.2.2 a starší, pak na místě kde má být enum stačí použít:
<bean id="person" class="cz.xxx.Person">
<property name="type" value="STUDENT"/>
</bean>
Jednoduché, ale moc šikovné. Tak to mám rád.

13. října 2008

jOpenSpace 2008 - Dokumentace - nezatracujme FOP

A máme tu pokračování inspirované poslechem audio záznamů z jOpenSpace konference. Již jsem psal o ORM a nyní mé poznámky k tématu Dokumentace. Toto téma mě opravdu hodně zajímalo, protože jsem měl dojem, že na našem projektu dokumentace moc nevzniká a hrozně mě zajímalo, jak to dělají druzí.

V podstatě vývojářskou dokumentaci neděláme žádnou, takže pro nováčka může být dost obtížné se v kódu zorientovat. Proč to tak je? No v podstatě se snažíme psát přehledný a jasný kód, což je pohlídané především systémem review, který používáme (o něm více v příštím příspěvku). Navíc je to vše umocněno skutečností, že mé schopnosti orientovat se v kódu jsou nadstandardní a protože podle sebe soudím tebe, myslím si, že to dovedou všichni. Ve výsledku se ukazuje, že to chvilku trvá, ale ve výsledku je struktura aplikace poměrně dobře pochopitelná. Hlavním problémem stejně zůstává skutečnost co aplikace dělá a proč, a o tom bych dokumentaci psal hodně nerad, protože by to bylo na hrozně dlouho.

Byl jsem potěšen, jak i v tomto ohledu je tvorba naší dokumentace v souladu s okolím. Především pro uživatelskou dokumentaci používáme DocBook, který v poslední době editujeme pomocí XMLMindu. A nyní se začínáme malinko odlišovat. Obecně se hovoří o různých způsobech převodu DocBooku do výstupního formátu. Zde jsme šli standardní cestou a tou je XSLT transformace, kterou získáváme jak manuál v HTML tak v PDF (přes FOP). Protože jsme si vybrali jenom podmnožinu DocBooku, kterou podporujeme, máme vlastní šablony, které nejsou komplikované a vše funguje ke 100% spokojenosti. Konfigurace fontů, která byla zmiňovaná jako komplikovaná, zas tak složitá není (v porovnání např. s iTextem je srovnatelně složitá).

Samozřejmě FOP má své mouchy a nedostatky, ale současné verze se od 0.20.5 liší a dosahují uspokojivých výsledků. Navíc se FOP docela vyvíjí a každá nová verze je lepší než ta předchozí.

Jako podpora nanáviděných formátu od nejmenované firmy, bylo zmíněno, že uživatelé stejně vyžadují Wordový dokument, který umějí zpracovat a proto nejde DocBook použít. To není pravda, protože převod DocBooku do RTF není problém a pak pomocí revizí zanést změny zpět do DocBooku také není složité.

Z diskuse je zřejmé, že s dokumentací se bojuje všude a najít vyvážení mezi dost a né příliš záleží projekt od projektu. Ale obecně platí, čím více dokumentace jsme schopní vygenerovat na základě jednoho zdroje (ať už je jím DocBook, kód, UML model atd.) tím lépe pro všechny zúčastněné. Jakmile začneme přepisovat myšlenky z UML, či z kódu do dokumentace je to cesta do pekel, dřív nebo později se dokumentace rozejde s originálem (a když se nerozejde, bude nás to stát velmi vysoké úsilí).

5. října 2008

jOpenSpace 2008 - ORM - Hibernate a klientská aplikace

Konečně jsem se dostal k tomu, abych si poslechl audia z akce jOpenSpace. Pro zájemce bych odkázal především na blog otce Fura.

Konference musela být jisto jistě velmi zajímavá a prospěšná. Moc mě mrzí, že jsem se nezúčastnil, ale mohu si za to především sám, protože jsem zasklil call-for-papers v konference java.cz. Dám si závazek, že napřezrok se to již nebude opakovat.

První záznam je z diskuse o ORM. Nosným tématem je použití Hibernate v klientských aplikacích. Problematické v tomto případě je, že Hibernate session object není thread-safe a Hibernate si hodně zakládá na lazy nahrávání čehokoliv (tj. k nahraným objektům je připojena session a atributy, asociace se dohrávají až v okamžiku, kdy je potřeba).

Klientské aplikace napsané v AWT, Swingu nebo SWT mají jedno velké specifikum oproti aplikacím webovým. Tím specifikem je jednovláknovost těchto knihoven. Tj. kód, který přistupuje k UI tak musí činit z UI vlákna (ve webové aplikaci má každý request své vlákno - skoro vždy). Z tohoto vlákna tedy není vhodné provádět náročné akce, protože dojde k zamrznutí UI. A jak v tomto prostředí řešit přístup k datům (pomocí Hibernate a jeho nesynchronizovaného objektu session)?

Varianty jsou v podstatě tři:

  1. používat session pouze z UI vlákna
  2. používat session pouze z jiného vlákna
  3. používat session jak z UI tak z jiného vlákna
První varianta je špatně, protože provádět dlouho trvající dotazy z UI vlákna zhorší odezvu UI. Druhá varianta je ideální, ale má jedno úskalí. Jak zařídit nahrání všech vlastností a vazeb objektu, které budou v UI potřeba tak, aby již nebylo nutné dotahovat žádná data (tj. jak obejít Hibernate laziness). Třetí varianta spočívá v nahrání dat v jiném vlákně a následném nahrání lazy atributů a vazeb z UI vlákna (dotahují se, až když k nim UI přistoupí a to tak učiní z UI vlákna).

Ještě je nutno zmínit jak to bude se správou session objektů. V prvním případě nám stačí jedna session, protože ji používáme pouze z jednoho vlákna. Pro druhý případ může session potřebovat víc, ale ve své podstatě to záleží na počtu výkonných vláken pro provádění komunikace s DB přes Hibernate. Ve třetím případě určitě potřebujeme pro každou událost (řekněme, že se jedná o obdobu requestu v klientské aplikaci) vlastní session, protože ji použijeme pro nahrání objektů a případné další lazy nahrávání z UI vlákna. Tyto 3 strategie je možné komplikovat dodáním synchronizace a využitím menšího počtu session objektů.

Mě osobně se nejvíce zamlouvá druhá varianta, protože veškerá režie s nahráním dat je mimo UI vlákno. Ale jak nahrát vše co je potřeba? Velmi jednoduše. Stačí, aby model každého UI prvku implementoval speciální rozhraní, které bude vracet ty atributy a vazby, které bude pro své vykreslení potřebovat. Díky tomuto kroku a stromové struktuře UI elementů stačí získat tento seznam pro model root elementu (ten se rekurzivně poptá u svých potomků). Inicializaci lazy atributů a vazeb je možné provést pouhým přistoupením k nim pomocí get metod (nebo pomocí metody initialize).

Bohužel klientskou aplikaci jsem již hodně dlouho nedělal, takže tento návrh je pouze z mé hlavy a né z praktické zkušenosti.