30. 10. 2014

Jaký byl pražský GeeCON 2014

Ve dnech 23.-24.10.2014 jsem navštívil konferenci GeeCON, jež byla po řadě ročníků v Polsku pořádána poprvé v Praze. Multikino CineStar na Černém mostě vytvořilo opět nezaměnitelnou atmosféru a na přednášky i doplňkový program konference byly velmi příznivé ohlasy. Ačkoli jsem z naší firmy jel sám, na místě jsem potkal řadu známých tváří z minulosti (především z nedávno skončené nekonference JOpenSpace). Přece jen bylo znát, že konference je v Česku, i když zastoupení polských účastníků bylo poměrně značné.

Sponzorem tohoto článku je společnost Y Soft, které s radostí děkuji za volňáska na konferenci.
Y Soft – platinový sponzor konference a mezinárodní společnost nabízející unikátní tiskové řešení (software i hardware), které umožňuje společnostem a organizacím efektivně kontrolovat náklady, snížit plýtvání, zvýšit komfort uživatelů a pozitivně působit na životní prostředí.

Kdo tedy pro mne byly největší hvězdy konference?

Oleg Šelajev – Unlocking the Magic of Monads in Java 8


Monáda je pojem, o který zakopl každý, kdo o funkcionálním programování aspoň něco málo sleduje a většinou to mají programátoři spojeno s něčím zcela odtrženým od reality, vědou a abstraktní matematikou. Oleg měl celou přednášku na hlavě čepici (černý kulich) s velkým bílým písmenem λ na čele. A to je s nadsázkou asi tak vše, co měla přednáška s matematikou společného. Nejprve se vtipně zeptal, kdo je tady proto, aby se dozvěděl něco o monádách (téměř všichni), a kdo proto, aby ho kontroloval, zda vše říká dobře (jeden člověk zvedl ruku). Na prezentaci bylo vidět, že naprosto realisticky odhadl, jaké mají jeho posluhači o monádách povědomí.

Nás, kdo děláme pouze s Javou, si navíc jistě získal i tím, že před povídáním o samotném tématu nahlas pojmenoval současnou situaci, kdy se z těchto věcí až příliš dělá jakási magie či nástroj zasvěcených. Konkrétními projevy tohoto fenoménu jsou např.: vysvětlování na jazyku s divokou syntaxí (Haskell, Scala), nespočet tutoriálů, které sklouznou moc brzy na hloubku, "machrování" co nejsložitějšími vytrženými matematickými definicemi v popularizačních přednáškách nebo vtipkování – např. tato parodie nebo známý aforismus o nemožnosti vysvětlit monády (o něm sice nemluvil, ale zařadil jsem ho sem vzhledem k tomu, jak se retweetoval můj tweet, rovněž předchozí odkazy doplňuji sám). To vše rozhodně nenapomáhá, aby se do toho zvenčí člověk dostal a podle přípravy prezentace se zdá, že si toho Oleg byl vědom.

Již několik let jsem na žádné přednášce nezažil, že by se speaker tak často ptal posluchačů, zda rozumějí a může jít dál. Ukázalo se nakonec, že to není tak hrozné, pokud se to vysvětluje na nám známé javovské syntaxi a jde se postupně. Monádu si lze v Javě představit jako návrhový vzor realizovaný generickým typem Monad<T>, který má 3 základní metody: tvorba Monad<T> z T, transformace Monad<T> na Monad<R> a vrácení hodnoty T. K čemu je to dobré? K vyjádření sledu kroků, kde na konec každého z nich navazuje další volaný přes callback. Zatímco řešení bez použití monád vede s rostoucím počtem kroků k zesložitění zápisu, jež je zapříčiněno nabalujícím se vnořováním callbacků, použití monád zachová lineární a tím relativně čitelný zápis (není náhoda, že v Javě jsou v módě fluent API, pro navazování monád je to přirozený prostředek). Oleg to vysvětloval na vylepšeném Future (Promise), dalším praktickým příkladem je JDK8 Optional.

Nelze říci, že po odchodu z přednášky budu programovat monády jako když bičem mrská. I přes veškerou snahu nepoplašit matematikou Oleg jasně upozornil, že monády jsou matematický koncept a člověk otevřený matematice bude mít vždy v jejich pochopení náskok. Ale podařilo se mi získat představu a tento fenomén si zařadit. Z přednášky mám velmi málo zápisků, protože jsem od začátku správně tušil, že tady je třeba si sednout, vypnout se a jen koukat, nepsat ani netweetovat.

Útržky:
  • 30% účastníků přednášky již používá JDK8 na produkci
  • existují algebraické zákony pro monády – levá a pravá identita a asociativita
  • jazyky jako Haskell nebo Scala mají monády částečně podporované syntakticky
  • naopak u Javy (označoval ji za mortal platform) se musí řešit dodatečné issues, aby to fungovalo
  • JDK8 streamy nejsou monády

Lukas Eder – 2000 Lines of Java? Or 50 Lines of SQL? The Choice is Yours

Píšete hodně SQL volaného z Javy? Přijde vám normální, že JDBC se na SQL – ano, na tento statický jazyk s pevně definovanou syntaxí a typy – dívá jen jako na pouhý řetězec? Mně už dlouho ne, ani mě nebaví pořád slepovat SQL ze Stringů nebo tahat z resourců. Podpora v IDE je slabá záplata (uznávám ale, že nápověda v SQL v IntelliJ IDEA je cool), hypotetické přidání multiline String literals do Javy by sice bylo o něco lepší, ale pořád se typová kontrola ztrácí.

Na tuto skutečnost cílí knihovna JOOQ (pozor, nečte se to "jé ó ó kvé" ani "džej ou ou kjů", ale prostě "džúk"). Geniálně jednoduchá myšlenka konstrukce SQL dotazů pomocí javovského fluent API. Lukas je skromně vystupující speaker, jehož přednáška mne lákala především kvůli této knihovně. Paradoxně jsem si nakonec odnesl z přednášky mnohem víc, i když se o JOOQ téměř nezmínil. Šel totiž do hloubky a zaměřil se na to, proč vůbec v dnešní době ORM frameworků zůstat u starého dobrého SQL. ORM frameworky totiž řeší především problém persistence dat. Dotazování se je pro ně až sekundární problém. Nadměrný důraz na dotazování může být symptomem, že ORM nástroj není zvolen resp. používán správně. Pokud zmigrujeme projekt, jehož business logika byla implementována na úrovni SQL, na ORM řešení, a následně potřebujeme dělat dotazy, máme v podstatě dvě možnosti. Buď zůstaneme u SQL, z nějž se stane pouhý String, a tím obětujeme typovou kontrolu. Nebo přistoupíme na dotazování se přes nějaký objektově dotazovací jazyk, což zpravidla vyhovuje u jednoduchých dotazů. U složitějších začíná být přínos sporný jak co do srozumitelnosti, tak výkonu, v porovnání s SQL stojícím na desetiletími ověřené relační algebře. Sklouzává to k mnohem méně efektivní implementaci, případně i k nějaké formě postprocessingu v aplikaci (to je těch 2000 řádek Javy v názvu přednášky). A typová kontrola jde do kytek stejně.

Z tohoto úhlu pohledu tedy vychází SQL jako velmi cool prostředek. Má velkou univerzálnost (zmínil například LHC, jinak Lukas je právě ze švýcarského Zürichu), stojí na spolehlivých základech teorie množin a přesto je ukecaný a tím srozumitelný i lidem-neprogramátorům typu operations, business analytik nebo doménový expert. Mimochodem podle dělení Martina Fowlera bychom na SQL mohli nahlížet jako na externí javovské DSL, JOOQ by pak bylo odpovídající interní DSL.

Většina přednášky byla tedy věnována výhodám SQL. Lukas se zaměřil výhradně na Oracle, což mi nevadilo, protože ho používáme. Ukázal, co vše se dá pomocí Oracle SQL vyjádřit a jak může deklarativní přístup přinést kód, který je stručnější a využívá správnou úroveň abstrakce. Příkaz merge, selecty v selectu, tuples a row value expressions, klauzule with, rekurzivní with, analytické funkce a klauzule model – to vše jsou konstrukty, které na první pohled vypadají jako COBOL, ale po osvojení jsou čitelné a říkají, co se má dělat, nikoli jak. Vše ukázáno na jednoduchém příkladu selectu, který vracel aktuální zůstatky účtu z tabulky jednotlivých transakcí, podloženo explain planem.


Útržky:

  • calculations should be done close to the data
  • SQL má odlišnou sémantiku díky 3hodnotové logice (puzzlík s NULL)
  • klauzule model – selectuje z db podobně jako vzorce v excelu
Po přednášce jsem ještě za Lukasem došel současně s dalším zájemcem. Lukas nám ochotně ukázal na svém notebooku demo ukázku JOOQ v akci a vnitřnosti kódu – jak řešili ochranu před SQL injection, inlinování hodnot parametrů, fígly s generikami zajišťující typovou bezpečnost a fluent API, napojení na JDBC, injektnutí dodatečné konfigurace (např. jak zajistit generování všech SQL identifikátorů v uvozovkách). Zajímavou debatou jsme strávili ještě 3/4 hodiny, takže následující slot přednášek jsem kompletně vynechal, ale rozhodně toho nelituji.

Anton Arhipov – Having fun with Javassist


Po Olegu Šelajevovi další JRebel. Javassist je knihovna pro manipulaci s bytekódem. Povídání plné praktických ukázek, jak se napíchnout na běžící aplikaci a změnit v ní třídy za běhu, živá ukázka spuštění Java agenta.

Java agent je třída, která má metodu public static void premain(String args, Instrumentation instrumentation). Nejedná se tedy o interface, ale o předpis pro signaturu statické metody, podobnou situaci důvěrně známe z public static void main(String[]). K jejímu spuštění dochází po startu JVM ještě před vlastním nahráním aplikace (nutno upravit manifest a spustit JVM s příslušným přepínačem). Parametr typu Instrumentation je vstupní branou k instrumentation API java.lang.instrument, které umožňuje pomocí metody ClassFileTransformer.transform modifikovat bajty třídy po jejich načtení ze souboru, ale před nahráním třídy do JVM.

A právě implementace metody transform je velmi jednoduchá, pokud použijeme Javassist. Ten od metody převezme pole bajtů a pomocí relativně vysokoúrovňového rozhraní se dostane až na modifikovanou metodu, do níž se vloží přidaný kód jako Java řetězec (ukázka).


Útržky:
  • transformují se classy, ne zdrojový kód
  • use casy: profilování kódu, optimalizace
  • ukázka "logování": když nemůžeme změnit třídu a chceme na konec metody přidat System.out.println(parametr)
  • odkazování se na parametry přes index ($0, $1...)
  • složitější změny pomocí method.instrument(new ExprEditor() {...})
  • ukázka generování proxies, napojení na metodu finalize() ukáže, kdy byl objekt uklizen
  • princip "A tool shouldn't force user into specific programming model" (to je i vlastnost AOP)
  • přidání příkazu na konec metody m se dělá pomocí m.insertAfter("{System.out.println(\"x\");}",true), kde druhý parametr říká, zda to přidat do finally (a tedy volat i při opuštění metody výjimkou) anebo zda přidat tak, aby se to volalo jen při řádném opuštění metody. Helemese – kolik pouček říká, neřiďte takově věci boolean parametry a i takový cool produkt to dělá a nikomu to nevadí? Mně tedy taky ne, poučky nejsou žádné dogma. Pro více info viz javadoc, je zajímavý i kvůli dalším metodám.
  • další ukázky JRebel pluginů pro reloadnutí Spring a Hibernate konfigurací
  • injection lze i do konstruktoru
  • pomocí třídy VirtualMachine lze agenta nahrát i do běžící JVM (mindmapa pojmů), roli metody premain přebírá metoda agentmain.
  • zajímavost: jeden bystrý posluchač v publiku položil přednášejícímu šťouravou otázku, sice ji už nedokážu zreprodukovat, ale přednášející se musel přiznat, že v podobě uvedené v prezentaci to Javassist nepodporuje a že si museli v ZeroTurnaround udělat vlastní hack Javassistu.
  • přednáška zmínila i další nástroj ZeroTurnaroundu: XRebel, kde manipulaci s bytekódem využívají tentokrát k profilování aplikací.
Celkově se jedná o velmi zajímavý nástroj, který stojí za to si osahat. Zvažuji, že místo dlouhého povídání uspořádám spíš malý hands-on lab, protože toto je téma, které si člověk dobře zažije až při experimentování.

Neal Ford – Functional Thinking in Java 8, Clojure, Groovy, and Scala

Přednáška částečně podobná této z Osla 2011, stejný příklad s NumberClassifier, stejný font, stejné čtverečky (psal jsem o ní už v září). Pouze přešel z poněkud akademické knihovny FunctionalJava na JDK8 streamy. Je vidět, že Neal, jak jezdí po světě evangelizovat funkcionální paradigma, své přednášky úspěšně recykluje, ale vůbec mi to nevadí. Přednáška vynikala přehledností a důrazem na smysl paradigmatu i jednotlivých kroků. Paradigma je těžší věc než jen naučit se syntax.

Útržky:
  • rozdíl mezi OO a funkcionálním přístupem: OO makes code understandable by encapsulating moving parts, FP by minimizing moving parts (Michael Feathers)
  • ukázal imperativní řešení problému, pak řešení pomocí "less functional version" (vzájemně volané statické metody) a až nakonec plně funkcionální
  • hezké grafické znázornění základních stavebních bloků funkcionálního programování (kolekce něčeho, co vypadalo jako brambory): filter, map, flatten, flat-map, reduce, fold-left/right ...
  • puzzlík demonstrující, že v případě zprava asociativní reduce operace nemusí jít výsledek intuitivně odhadnout: operace odečtení sousedních prvků volaná jako fold-right na seznamu 1..10 vede na odčítání 8-9, 7-(-1), 6-8, 5-(-2)... s konečným výskedkem 5
  • Bentley-Knuthův problém (1986) – umělá vzorová úloha na výběr a řazení slov ze zadaného textu. Z hlediska funkcionálního paradigmatu zajímavá tím, že je to typ úlohy řešitelný sledem kroků map a reduce. Více info např. zde nebo v článku Neala Forda.
  • currying ... nevolá transformaci, pouze popisuje skládání jiných transformací
  • aplikace curryingu: mám funkci volající db, která dostává mj. jméno a heslo. Po přihlášení uživatele je možné tuto funkci curryfikovat na jinou funkci pro zafixované jméno a heslo, argumenty druhé funkce už jsou pouza zajímavé vstupy pro volání konkrétního dotazu.
  • laziness – konkrétní projevy: odkládání výpočtu výrazu (např. metoda pro rozdělení řetězce produkuje jednotlivé prvky, až když o ně klient požádá, ukazoval to na příkladu v Clojure, ale např. Guava Splitter to dělá taky), vytváření nekonečných kolekcí
  • vliv FP na design patterny: např. Command zcela zaniká, je pohlcen paradigmatem; Flyweight zůstane, ale díky memoizaci může být implementován zcela odlišně než při objektovém paradigmatu. (více info)
Přednáška většinou neříkala nic, co by pro mne bylo zcela nové, ale byla výborná pro uvědomění si souvislostí. Poměrně podstatná věc zazněla v samém závěru: jestliže jazyk podporuje více paradigmat, přináší to větší nároky na disciplínu a code review. Ano, pokud bylo dosud možné napsat program v Javě imperativně i objektově, tak teď přibyde ještě třetí možnost – funkcionálně.

Oliver Gierke – Whoops! Where did my architecture go?

Co tvoří vaši aplikaci, pokud byste ji měli rozdělit podle toho, jak ji vidí programátor a naopak podle toho, jak ji vidí zákazník? To je téma, kolem nějž kroužilo povídání Olivera Gierkeho. Project lead Spring data přišel se slibným abstraktem a po dobré zkušenosti z loňského GeeCONu jsem si jeho přednášku vybral i letos a nelitoval. Začátek byl poměrně obecný o architektuře jako takové. Zajímavá byla ukázka grafu závislostí mezi jednotlivými moduly pro junit. Velmi zajímavý byl popis konceptu layers a slices a motivací pro ně. Na něj navazovaly konkrétní praktické ukázky, jak architekturu realizovat a udržovat v codebase. Sympatické bylo, že vše popisoval na plain Javě, neexistoval sebemenší náznak lock-inu na konkrétní technologii nebo framework.

Útržky:
  • provokativní slajd "if you think architecture is expensive, try no architecture"
  • Architecture is like weather, you can't have none.
  • makroarchitektura = jak systémy spolu komunikují, mikroarchitektura = jak jsou vystavěny uvnitř
  • znejte závislosti
  • co je single unit of understand/change
  • jaké riziko přináší změna - v případě znovupoužitého modulu: co změna rozbije, v případě copypastu: jaké jsou náklady plynoucí z rozdělení
  • layers – repository, servisy,..., nezajímají zákazníka, dobře jim ale rozumějí programátoři
  • slices – uživatelé, účty..., klíčové business koncepty, kterým dobře rozumí zákazník, pro programátory nové a těžko srozumitelné
  • programátoři tíhnou k tomu si projekt rozdělit podle kritéria, kterému lépe rozumí
  • závislosti jsou jak mezi layers, tak mezi slices, mezi slices je více podstatná
  • když se dají závislosti vyjádřit jako acyklický graf, je to kvalitativně lepší než když existuje v závislostech cyklus
  • u layers by závislost DAO na service byla nápadná, cyklické závislosti mezi slices jsou lépe přehlédnutelné ("I see tons of systems where Account calls Customer and Customer calls Account and nobody fucking cares.")
  • core systému je taky slice, závislosti se sbíhají na něm
  • nástroje pro analýzu codebase: JDepend, Sonar, Sonargraph (dříve SonarJ)
  • jak architekturu namapovat na package? přístup layers first, slices first, slices only
  • layers first – prosakují interní vlastnosti slice, které měly zůstat skryté před okolním světem
  • slices first/only – spousta věcí může být jen package-private. Compiler tak zadarmo pomáhá zavádění architektonických pravidel.
  • začněte s co nejméně packagi a co nejnižší možnou viditelností, rozhodnutí o zvýšení viditelnosti odložte na později
  • OT: prezentovaný projekt měl pěkné formátování logů (zkrácení packagů na 1. písmeno a dostatečná šířka všech sloupců, že log vypadá jako tabulka)
  • zajímavý Maven plugin: JQAssistant – umožňuje prozkoumat projekt a vyhodnotit parametry jeho kvality podle vlastního nastavení (prozkoumat)

Nebylo mnoho lidí, kteří se přihlásili k přístupu slices first/only, ale argumenty pro něj mi přijdou velmi přesvědčivé. Doufám, že si příště při návrhu struktury packagů co nejvíce účastníků vzpomene na tuto přednášku. Pozor na rozhodování od stolu, zaměřit se na měření správných veličin potřebných pro příslušné rozhodnutí. Prozkoumat nástroje (nebo v jednoduchých případech i napsat jen skripty) pro analýzu codebase, příp. databáze. Zmapovat závislosti, získat lepší představu, co smazat a jak se blokují práce. Dbát na omezování viditelnosti.

Martin ŠkurlaWhen assertThat(you).understandUnitTesting() fails

Původní plán jít na paralelní přednášku Václava Pecha Speaking your language jsem přehodnotil a vybral si téma, které je pro mne aktuálnější. Byla to přednáška nabitá od začátku do konce poučkami, tipy, patterny, antipatterny a nejrůznějšími radami. Přesněji: do půl hodiny po konci – přednášející značně přetáhl rozsah a měl svým způsobem štěstí, že měl poslední slot prvního dne a nikdo nás nevyhazoval. Řada rad mi přišla trochu kontroverzních, nicméně nelze popřít, že celé povídání mělo myšlenku, se kterou se nemusím v našich podmínkách ztotožnit, ale která mi přišla svým způsobem konzistentní. Textový soubor se zápisky mám asi 2x delší než je průměrná délka zápisků z ostatních přednášek, budu se snažit to sem na blog nenatahovat.

Obecné:
  • rozdíl mezi čitelností a srozumitelností testu. Čitelný = dokážu to přečíst jako program a dává to smysl. Srozumitelný = čitelný plus rozumím doméně testovaného problému. Čitelný test nemusí být ještě srozumitelný.
  • pro metodu s názvem nameOfTheMethod je antipatternem nazvat testovanou metodu testNameOfTheMethod, lépe je složit název ze 3 částí: testovanéChování_vstup_výstup.
  • pro třídu s názvem NameOfClass je opět antipattern nazvat testovací třídu NameOfClassTest. Testovací kód vztahující se k jediné třídě nemusí nutně být v jediné testovací třídě, v jedné testovací třídě by měla být logická skupina testů testující jedno pravidlo nebo jev, které API testované třídy nabízí (např. test na hodnoty atributů, test na pořadí aplikování pravidel).
  • různé test smells: podmínky v testu (např. na počet jader procesoru) navozují více exekučních cest – nahradit více testy nebo pomocí JUnit assumptions; cykly v testu představují schování více testcasů do jednoho – nahradit za parametrizované testy
  • SUT = System Under Test = testovaný objekt. Asserty by se měly vztahovat pouze k SUT, ne k jinému objektu. (To bychom jako pak neměli používat mocky?)
  • používání assertTrue, assertEquals se považuje za bad practice, odůvodňoval to nesprávnou úrovní abstrakce, takové asserty obvykle testují hodnoty proměnných a samotný smysl testu je pak musí napsat do dlouhého sofistikovaného řetězce v message u assertu
  • Martin preferuje custom assertions – fluent API pokračující za assertThat(SUT) a umožňující si definovat i vlastní matchery. Custom Assertions zachovávají kontext, i chybová zpráva se sestaví automaticky. Kdybych četl název přednášky pozorněji, přišel bych na to dřív.
  • nepoužívat logování a System.out.println, nikdo výstupy testů nečte
  • pozor na sofistikované testy složitých situací jako GC, memory leaků nebo latence odezev (A co testování threadů?)
  • nepřehánět to s vysušováním – u unit testů je opakování stejného kódu v určité míře zdravé a jeho odstranění může zakrýt některé okrajové případy, stejně tak literály by se měly extrahovat do konstant pouze u hodnot nedůležitých pro test a jinak by měly zůstat v testu. Teď mě nenapadá konkrétní případ, ale myslím, že jsem na to někdy narazil; každopádně zdlouhavě napsané testy se mi špatně čtou a spíš se snažím je omezovat.
  • inicializace SUT v @BeforeMethod (TestNG ekvivalent junitového @Before) je prý taky antipattern, protože neodpovídá vzoru Arrange-Act-Assert – chybí část Arrange. (Tedy toto nepovažuji za nevýhodu a rád inicializaci nechám v @Before metodě, vždyť pro každou testovací metodu mi framework vytvoří novou instanci testu a na ní zavolá metodu @Before – proč bych měl jít proti filozofii testovacího frameworku?)
  • ukázka deklarativního testování: testovací metoda oanotovaná anotacemi s řetězcovými parametry tak, že řetězce lícovaly svislítky pod sebou a vypadalo to jako tabulka. Připomínalo mi to Spock (o něm byla přednáška na GeeCONu loni). 
  • paralelně vykonávané testy – smysl paralelního běhu není v lepším výkonu, ale lepší izolaci, testy nutí mít dobře vyřešen sdílený stav.
Co bych přednášce vytkl, je paušální prohlašování mnoha věcí za antipattern, zdrženlivost ve stylu "do toho se radši nepouštějte" a šermování mnoha principy, kde hledání kompromisu mezi nimi mi už přijde pouze jako akademický problém. Testy nejsou na hraní, považuji je za pouhý nástroj, jak lépe dosáhnout business hodnoty očekávané zákazníkem, nikoli za její přímou součást. Jsem proto rád, když programátoři nějak sami dojdou k vědomí jejich užitečnosti a začnou je psát, a porušení nějakých pouček zas tak moc neřeším. Máme testy i proti databázi, testy, kde stačí nespadnout na výjimku, i testy, kde se např. čeká na vlákno. Na druhou stranu: možná je lepší, aby přednáška propagovala ideál čistého kódu a já jej zde v článku zlehčoval, než aby mlžila a neměla jasné poselství. Takže celkově hodnotím přednášku pozitivně, Martin to má srovnané a ví, co chce.

Ostatní

Neal Ford – This is water

Úvodní keynote připodobňující naše prostředí k vodě, ve které plujeme, ale kterou nevidíme. Připodobnění tří věcí z fyziky k aspektům tvorby softwaru: μ (koeficient tření – interakce mezi různými subsystémy, continuous integration), δ (změny a diverzita), λ (veličina je funkcí jiné veličiny). Obecné povídání, zajímavé útržky:
  • metawork is more interesting than work (proč existuje tolik frameworků)
  • yesterday's best practice is tomorrow's antipattern
  • build architecture that support, rather than punish
  • počítání indexů od 0 nebo od 1? V céčkových jazycích počítáme od nuly, protože se tak lépe sčítají pointery. Protože se to zažilo, už jsme se naučili to považovat za přirozené, i když v normálním životě to přirozené není. (Také neříkáme: Kolik máte nohou? Nultá, první.)

Bruce Eckel – Do Languages Matter?

Nelze říct, že bych na některé z přednášek zaznamenal nějak konfliktní či kontroverzní prosazování některého jazyka či paradigmatu. Nicméně jen z toho, jak pestrá je nabídka současné doby, je zřejmé, že i programovací jazyky soupeří o pozornost technicky orientovaných lidí a snaží se uspět na trhu stejně jako kterýkoli jiný produkt. Pokud bylo úmyslem organizátorů tuto situaci vyvážit přednáškou někoho, kdo má charisma autority a dokáže hovořit s příslušným nadhledem, pak se jim to výběrem Bruce Eckela rozhodně podařilo. Závěrečná keynote byla provedením historií programovacích jazyků v kontextu hlavní otázky proč (nikoli jak nebo co). Zajímavé vyzdvihnutí toho, jak každý přechod provázel odpor těch, kdo považovali nový přístup za neschopný vyrovnat se výhodám předešlého (zřeknutí se assembleru při přechodu na C, systém knihoven při přechodu na C++, zavedení VM a GC při přechodu na Javu, v současnosti automatizace paralelismu a správy stavu).

Markus Eisele – 50 new features of Java EE 7

Přehledová přednáška systémem 1 featura za minutu, navštívil jsem ji za účelem aktualizace povědomí o světě JEE, když normálně používáme Spring + Hibernate. JEE je samozřejmě rozsáhlý stack, takže tam bylo od každé části kousek. Dojem: JEE se modernizuje, používají se anotace, i když ukázce s Batch se věnoval Markus celkem dlouho a tu měl v XML.

Michael HeinrichsDo-It-Yourself Usability Design for Developers

Přednáška o UX, principy návrhu, the curse of knowledge, propagátor papírového prototypování.

Lightning talky

Heather VanCura o JCP, Krzysztof Debski a Fabian Förster o architekturách jejich aplikací.

Shrnuto

Na GeeCONu jsem byl celkově podruhé. Připojuji ještě pár postřehů z atmosféry konference:
  • Twitter – oproti loňskému roku jsem ho začal víc používat, je to skvělý nástroj pro sdílení postřehů a čerpání informací. Výborná věc jsou zvací tweety ještě před přednáškou, které mnohdy doplňují abstrakt a pomáhají se rozhodnout v paralelním programu. Účet na Twitteru měl snad každý speaker a častokrát na něm po přednášce uváděli odkaz na prezentaci, také bylo velmi fajn jim přes tento kanál poděkovat. Sběr tweetů s hashtagem #geecon se hodí i k přejití na vedlejší zajímavější přednášku.
  • Nesmím zapomenout na dobře zvládnutý catering a toleranci k nošení jídla na přednášky do sálů.
  • Zásuvky pro notebook byly po stranách a občas byly všechny obsazeny, takže příště si beru rozdvojku.
  • Doplňkový program: stánky, soutěže a GeeCoiny – pro zábavu a zpestření je to fajn, ale kvůli tomu na GeeCON nejedu. Když už tam jsem, odevzdám dotazníky s rébusy do srandasoutěží a vezmu dětem domů hračky, aby měly radost. Ale když se losovačka soutěže kryje s pásmem dotazů po zajímavé přednášce, tak žádný problém, losovačka má smůlu. Je to jen hra a člověk se nesmí brát moc vážně :-). Nahlíženo z tohoto úhlu, přesně tak jsem to měl i s geecoiny, necítil jsem motivaci účastnit se "miningu" a počítat si po každé přednášce svoje penízky. Velmi kreativní nápad, ale pokládám dotaz, abych se něco dozvěděl a ne abych dostal bílý geecoin. A moc speakerů o jejich rozdávání taky explicitně nemluvilo.

GeeCON 2014 Praha byl velmi podařená akce, za kterou vyslovuji organizátorům velký dík a uznání. Pozitivně hodnotím, že přednášky byly o řemeslném programování s hojným množstvím konkrétních ukázek kódu a reálným sdělením, případně o zkušenostech z konkrétních projektů. Takové jsou pro mne víc obohacující nežli přednášky, kde slajdy obsahují pouze metaforu, zábavný obrázek, provokativní otázku nebo jiný oslí můstek k nosnému tématu, o němž speaker mluví pouze z hlavy a nemám to před sebou. Z tématického hlediska byla zastoupena většina horkých témat okolo moderních trendů programování. Odcházet z konference s hlavou plnou nápadů, jak bychom to či ono mohli dělat lépe, to je přece její smysl?! A ten se podařilo naplnit.

5. 10. 2014

Lidi mají radši mail

V pátek večer jsem strávil zbytečnou hodinu hledáním, proč náš Jenkins server nechce vytvořit build, který jsem potřeboval nasadit na produkci. Příčina i řešení se nakonec ukázaly jako triviální: kolega před půl rokem přesunul Jenkins na lepší stroj (na kterém stačilo Jenkins službu pouze zrestartovat), ale na interní wiki byl stále uveden starý. Protože URL zůstalo stejné a ke všemu, co od Jenkinsu potřebuji, mi stačí webové rozhraní, neměl jsem až do včerejška potřebu připojovat se k počítači vzdáleně. Pro zjištění stroje jsem automaticky šel na wiki, na informační mail od kolegy jsem si pod tlakem nevzpomněl.

Taky používáte groupware systémy a přesto dostáváte nebo píšete spoustu mailů? Půjčím si formát výborného článku Lidi nečtou napsaného před půldruhým rokem Lukášem Křečanem. Můžete vypiplat písemný projev, ale pokud nepřitáhne a je dlouhý, vaše snaha přijde vniveč. Analogicky: můžete zavést groupware, ale když nepřekonáte bariéry, které brání lidem ho začít používat, neujme se. Proč? Protože lidi mají radši mail.

Příklady ze života: lidi raději pošlou informaci (když už vůbec chtějí informovat) mailem namísto aktualizace patřičné stránky na wiki. Nebo sice aktualizují wiki stránku, ale stejně o tom pošlou mail. To sám dělám, jelikož vím, že ne všichni odebírají novinky z wiki přes RSS. Nebo posílají mailem copypaste úryvku stránky z wiki namísto URL s #id příslušného oddílu a tento úryvek si začne žít vlastním životem. V issue trackeru je s velkou slávou založen nový projekt/task/bug, ale další informace, přílohy, upřesnění požadavků apod. se šíří mailem (dokonce jsem zažil i posílání souboru přes Skype). Nebo existuje mnoho dalších nástrojů – portál, sdílený disk, Google Drive... – a překážka je paradoxně v jejich velkém počtu a mnoha drobných omezeních: více různých přihlášení, restriktivní ACL, rozmlžené hranice jejich kompetencí. Co je pak jednodušší? Poslat mail.

Zdá se, že tu proti sobě stojí dva přístupy. Na jedné straně groupware – vymakaný software. Z logického hlediska uzná jeho výhody asi každý: centrální úložiště informace, různé způsoby prezentace, přístup "vše má své URL" (mj. tento přístup vedl i k popularizaci RESTu). Když ale dojde na to ho používat, objeví se psychologické bariéry – počínaje nutností spustit prohlížeč a konče samotným osvojením konkrétních dovedností (např. wiki markup) a UX daného nástroje. V jejich důsledku získá nástroj stigma těžkotonážního tanku, často bez ohledu na jeho objektivní složitost.

Na druhé straně je mail a obecně peer-to-peer systémy (např. Skype, ICQ a jiné IM aplikace) – jednoduché, každý jim rozumí. Abych udělal radost Dagimu, přirovnáno k armádě: když groupware systémy jsou tank, mail je Kalašnikov. Co na tom, že se maily hromadí a mám bordel ve složkách a tagách? Máme vyhledávače. Co na tom, že jednou odeslaný mail nelze opravit? Pošlu mail znovu resp. zeptám se mailem, jak je to správně. Co na tom, že nemám historii dokumentu? Mám ji trochu zachycenou ve stromu konverzace a to většinou stačí.

Pro pozorování těchto rozdílů ale není třeba chodit do práce, stačí se rozhlédnout po soukromé emailové schránce. Máte tam skupinu nazvanou Rodina, Třída nebo Kamarádi? Jiný příklad: v našem týmu ultimate frisbee projednáváme týmové záležitosti (pokud spolu nejsme osobně) přes webovou aplikaci tymy.cz. Podotýkám, že aplikace umožňuje zřízení diskuse omezené účelem a přizvanými osobami. Přesto se občas stane, že se někdo utrhne a nějaký problém řeší rozesláním hromadného mailu. Ostatní pak ochotně odpoví přes Odpovědět všem. Výsledkem je dlouhá nečitelná konverzace s předměty ve tvaru Re: Re: Fwd:... "Proč jste to neřešili přes týmy?" "Moc složitý." Lidi mají radši mail.

Jako vývojář tíhnu k omezování mailu a preferování groupware řešení. Při programování si taky neposíláme změny mailem, ale používáme verzovací systém, proč stejný přístup nepřenést i na jiné druhy práce? Loňský GeeCON byl zakončen přednáškou How to do Kick-Ass Software Development, která se tohoto tématu taky dotkla a maily poměrně striktně zavrhla (viz slajdy 88-93; mj. jistě není náhoda, že přednášející Sven Peters je z Atlassianu – tvůrce issue trackeru JIRA). Nicméně stále budeme narážet na to, že někoho to prostě nepřesvědčí. A síla davu rozesílačů mailů je velká. Dobře to ilustruje zajímavý článek, jenž svého času vyšel na idnesu, o společnosti, která se rozhodla tomuto nadužívání mailů vzepřít extrémním způsobem – jejich zrušením. Nevím, jak to s ní dopadlo, ale struktura článku je v souladu s mým pozorováním: vzletné myšlenky, s nimiž nelze nesouhlasit, a poslední věta jako studená sprcha, která vrací do reality.

Neodvažuji se tvrdit, proč je tomu tak. Napadá mne např. vysvětlení, že člověk je tvor sociální; podobně jako je pro něj přirozené vyhledávat setkání s jinými lidmi, je pro něj přirozená i komunikace peer-to-peer. Lidi taky chodí cestou nejmenšího odporu. Nejsem hlubinný psycholog a následující domněnka může vypadat šíleně, ale možná by se dalo hledat i v "temnějších" koutech – mail více uspokojí lidský egocentrismus (posílám ho tolika lidem a ó všichni mě čtou) a lidskou ješitnost (je to přece můj výtvor), alespoň na podvědomé úrovni, když ne na vědomé. Tak jako tak, u groupware tím středem pozornosti nejsem já. Ani kolega. Je jím to společné dílo. Software. Dokument. Entita. Neživá studená věc. U mailu je na druhém konci drátu živý člověk. Možná proto lidi mají radši mail.

Abych se nedopouštěl zbytečné polarizace a vyhrocování, dlužno dodat, že oba přístupy mají jistě i mnoho společného. Způsob vyjadřování popisovaný v článku Lidi nečtou platí nejen pro maily. U obou přístupů se nerad setkávám s nadmírou překlepů a chyb. Oba vyžadují nějakou systemizaci obsahu. A ani hranice mezi nimi není ostrá – např. s diskusemi a mailovými konferencemi si "povídám" posíláním mailů, ale ve výsledku je vše v archivu na webu, čímž čerpám výhody groupwarového přístupu. Podobně i např. možnosti publikace blogpostu posláním mailu.

Lidi mají radši mail. Přiznávám, že v tuto chvíli nedokážu kategoricky říct: smiřte se s tím. Ale trávit příliš mnoho energie bojem za to nebo ono řešení taky nedává smysl. Kde vidím cestu? Dělat, co považuji za dobré. Pomáhat dobrým řešením se prosadit. Být nad věcí – mít na zřeteli cíl, komunikační nástroje jsou pouze prostředky. Ocenit, že lidi vůbec komunikují, i když je to mailem (natož mailování nějak kriminalizovat). Zůstat ostražitý a spoléhat na selský rozum. Máte taky podobnou zkušenost?

21. 9. 2014

Co se chystám navštívit na GeeCON Praha 2014

Za necelých 5 týdnů – 23.-24. října 2014 – se v Praze bude konat GeeCON, konference pro vývojáře a nadšence pro technologie. Nevím, zda je to pro ajťáckou pověst naší země, ale po šesti ročnících uskutečněných v jarních termínech v Poznani a Krakówě se tým organizátorů zřejmě rozhodl expandovat za hranice Polska a v témže roce uspořádat ještě jednu konferenci v podzimním termínu v Česku.

Protože jsem na jaře do Krakówa nejel, velmi jsem to uvítal. I přesto, že přednáškový program konference bude pouze dvoudenní (workshopy a open spaces jsou v samostatném dni předem – 22. října), slibuje akci nabitou informacemi a zajímavými osobnostmi. Hlavním partnerem konference je stejně jako v minulých ročnících YSoft – mezinárodní společnost nabízející řešení pro kontrolu a optimalizaci nákladů u tiskáren a multifunkčních zařízení.

Podobně jako na jaře zkusím uvažovat nad programem nahlas, v pořadí speakerů a témat, jak se mi jeví atraktivní (neřeším rozhodování v rámci časových slotů, schedule stejně ještě není dotvořen):

Neal Ford – Speaker a autor knih z Thoughtworks je v programu hned dvakrát – na opening keynote a ve čtvrtek 14.50. Když před několika lety začal vzestup funkcionálního paradigmatu, osobně jsem nejdřív tento trend nevnímal, funkcionální programování jsem měl spojené pouze se (jinak ovšem v podání doc. Koláře výborným) semestrem LISPu na ČVUT. Vědomě jsem tento hype pocítil právě po shlédnutí Nealovy přednášky Functional Thinking na JavaZone (vyšla s obdobným obsahem i jako článek). Neal Ford tedy určitě bude mít co říct a i když téma odpolední přednášky ještě není upřesněno, bude to pro ostatní paralelní přednášky určitě pěkná konkurence.

Lukas Eder: 2000 Lines of Java? Or 50 Lines of SQL? The Choice is Yours – Autor JOOQ frameworku, propagátor SQL. Náš projekt vznikl z legacy projektu, kde relační databáze je ústředním prvkem architektury a programátoři byli zvyklí aplikační logiku psát v PLSQL. Ať jsou nevýhody tohoto přístupu jakékoli, jisté je, že nepotřebovali ORM, nepotřebovali JDBC, zkrátka SQL měli kdykoli po ruce. S přechodem na 3vrstvou architekturu bylo nutné se vypořádat s novými problémy: embednutí SQL do Javy spojené se ztrátou kontroly při strukturálních změnách, volání dotazů a procedur, rozhodování, co nechat v PLSQL a co přepsat do Javy. Turbulentní období poznamenané opuštěním Ibatisu ve prospěch Hibernatu, se Spring JdbcTemplate v trvalé záloze. Protože ambicí JOOQ je zmenšit sémantickou mezeru, která vznikla zavedením ORM, je tento framework pro nás potenciálně zajímavý a chci se o něm a o tom, jak by překonal úskalí našeho projektu, dozvědět víc. 

Michael Heinrichs: Do-It-Yourself Usability Design for Developers – Velmi lákavá přednáška ze dvou důvodů: za prvé UX je úplně jiný soudek než ostatní "programovací" přednášky, za druhé konstatování "only few teams are equipped with a UX expert" sedí i na náš tým – nemáme specialistu vyčleněného pouze na UX (uživatelé systém samozřejmě testují a vyjadřují se k jeho ovládání, ale na druhou stranu často sledují pouze své zájmy a až donedávna, kdy jsme alespoň základní zásady formulovali, nám jednotící princip chyběl). Očekávám, že až u nás v interní prezentaci o GeeCONu budu povídat o této přednášce, bude ohlas.

Oliver Gierke: Whoops! Where did my architecture go? – Přednášející je project lead Spring Data, jeho přednáška o Spring Data v květnu 2013 byla pro mě jedna z lepších, i když jsem se NoSQL věnoval za život všeho všudy asi tak 2 dny (nebo právě proto). Tohle vypadá na obecnější architektonické povídání a témata, ke kterým máme blízko: rozdělení na moduly a package.

Oleg Šelajev: Unlocking the Magic of Monads in Java 8 – Zajímavá kombinace tématu (monads a Java 8), přednášejícího (šachový velmistr a milovník hádanek) a firmy, odkud pochází (ZeroTurnaround, tvůrci JRebel) – vypadá slibně. Hned v pátek v 9.00 – po ránu by to mohlo jít. Na loňském GeeCONu jsem přednáškám s podobným abstraktem dal šanci, doufal, že nebudou moc teoretické, a pak se spálil, ale jsem nepoučitelný optimista.

Anton Arhipov: Having fun with Javassist – Další JRebel, tentokrát na téma javassist (framework pro manipulaci s bytekódem). Těším se na praktické ukázky.

Gleb Smirnov: Java Concurrency Under the Hood – U Javy si celkem dobře vystačím s abstrakcí na úrovni JVM a moc pro svou práci nepotřebuju vědět, jak je to implementováno na úrovni zdrojáků OpenJDK. Lákání v abstraktu na "fun live demo" dá jen těžko pozapomenout na fakt, že to prostě bude bolet.

Václav Pech: JetBrains MPSSpeaking your language – Některé lokální problémy řeším interním (tj. postaveným na Javě) DSL. Nicméně z abstraktu se zdá, že bude spíš o externích DSL, se kterými by byla v našich podmínkách přece jen větší režie. Ale pokud bych přednášku navštívil, pak kvůli inspiraci.

Nikita Salnikov-Tarnovski: I bet you have a memory leak – Podle twitteru byly na tuto přednášku letos v Krakówě pozitivní ohlasy.
Ostatní: Nicolas De Loof: Docker: Zero to Hero (vývoj pro cloud), Jaroslav Tulach (dobrá zkušenost z přednášky na CZJUG, bohužel zatím bez tématu), Heather VanCura (také zatím bez tématu, ale dá se očekávat, že to bude něco o JCP na způsob přednášky na jaře, můj postoj z jara zůstává stejný). Za zmínku stojí i workshopy (Gradle a JDK8) a open spaces s Bruce Eckelem, které tvoří samostatný den.

A to jsem zmínil jen asi třetinu všech speakerů. Jako obyčejný účastník musím poděkovat organizátorům za odvahu uskutečnit premiéru GeeCONu v jiném místě a čase, a vyslovit uznání za úsilí udělat konferenci srovnatelnou s polskými, i přes jistou (a pochopitelnou) opatrnost v rozsahu. Přeju, ať se akce podaří a především se těším na viděnou v Praze!

9. 8. 2014

How to download all original photos from Picasa web

Simple situation: our friend family sent us URL to their photoalbum at Google Picasa and I was going to download it. Single photo can be downloaded from its detail page but AFAIK there is not direct way how to download whole album from browser. I know there is a Picasa client. Nevertheless, I don't like making my Program Files dirty of any programs which are not really necessary and rather prefer solutions where only browser is sufficient.

Since this approach was successful in case of similar Czech site rajce.net (EDIT 2020-09-12: original script got obsolete and was replaced, see following message and tweet), I tried to find similar advice. There are many cookbooks on the web but unfortunately, it would be time-consuming to check them all. I made couple of attempts and observe that either they are obsolete and don't work (as Google is probably changing it's API) or they download only small-sized image (full-sized image is available only in Flash panel where image URL cannot be easily discovered).


I came to final solution combining following 2 advices (URLs are intentionally made invalid for the sake of privacy):
  • showing RSS link as advised here produces simple page with only small-sized images with URL like https://lh3.googleusercontent.com/-W7d8yk40s8o/U5qi9sQyUKI/AAAAAAAAD6A/R4Tz5yC_03z/s288/IMG_1042.jpg
  • intercepting network communication in Firebug after clicking Download on photo detail shows that original image has very similar URL: https://lh3.googleusercontent.com/-W7d8yk40s8o/U5qi9sQyUKI/AAAAAAAAD6A/R4Tz5yC_03z/d/IMG_1042.jpg 
Hence the procedure is really simple if you have Firebug:
  1. Open album and click on RSS.
  2. Show Firebug console. 
  3. Copypaste following code into Firebug console:
    for (var i = 0; i < document.images.length; i++) document.images[i].src = document.images[i].src.replace(/\/s288\//,"/d/");
    
    This replaces images by original versions.
  4. Save complete page as foo.xht.
  5. Your images are in subdirectory foo_files. (remove unnecessary HTML, CSS, JS, title image and other stuff)

For completeness - scripts for other sites:

rajce:

$("#thumbs-container .thumb-img").each(function () {var a = $(this).css('background-image'); a = a.replace(/url\("(.*)"\)/g,'$1').replace(/\/thumb\//g,'/images/'); $(document.body).append("<img src='" + a + "' />");})
UPDATE: 
$("#thumbs-container .thumb-img").each(function () {var a = $(this).attr('src'); a = a.replace(/url\("(.*)"\)/g,'$1').replace(/\/thumb\//g,'/images/'); $(this).attr('src',a);}) 
clovekavira.cz:
Array.from(document.getElementsByClassName("grid-item")).forEach(function(element, index, array) {var img = element.firstChild.firstChild; var s = img.src.replace(/\/500x180\//,'/1920x1080/'); var nimg = document.createElement('img'); nimg.src = s; document.body.appendChild(nimg); }); document.getElementById('block-cav-content').remove();


10. 7. 2014

Číslování anonymních tříd

Že Eclipse JDT compiler dělá některé věci jinak než javac, je obecně známo – typickou odlišností je např. lepší inference generických typů při volání generických metod. Dnes jsem narazil na další vypečený případ, který stojí za blogpost. Naše aplikace je tvořena
  • serverovou částí, která běží v Tomcatu
  • klientskou částí, která se u uživatelů spouští přes Java Web Start, ale při vývoji na lokálním počítači ji spouštíme klasicky z IDE (při testování ji samozřejmě z testovacích prostředí spouštíme taky přes Java Web Start)
  • společnou částí – api.jar, který je sdílen serverem i klientem, obsahuje interfacy servisních metod a DTO objekty
Tentokrát jsem však potřeboval otestovat plnou funkčnost včetně spouštění přes Java Web Start, ale z lokálního počítače. Tento usecase byl trochu výjimečný, šlo o otestování spouštění pod jiným updatem JRE, než je na testovacích prostředích. Zbuildoval jsem tedy api.jar, klient i server, spustil jsem v Eclipsu Tomcat, vypublikoval do něj server (používáme WTP) a připravil klienta tak, aby se našel dle URL v JNLP souboru. Vše připraveno, musí to klapnout! Oops - nastává chyba:
java.io.InvalidClassException: Foo$2; class invalid for deserialization

Příčina

V api.jar existovala třída Foo, která v sobě měla dvě různé instance anonymních tříd (osekávám na minimalistický případ, ve skutečnosti jich bylo víc). Buildování aplikace provádíme Mavenem, který (přesněji maven-compiler-plugin) pro kompilaci tříd používá javac.Vypublikování do Tomcatu však WTP dělalo z adresáře, kam byl api.jar zbuildován pomocí Eclipse JDT.

Pozorování ukázalo, že v souboru Foo$1 je – podle kompilátoru, který jej vytvořil – pokaždé jiná třída. Totéž v souboru Foo$2. Server a klient tedy měly každý svůj api.jar, v něm stejně pojmenované třídy, ale s jiným obsahem. Server klientovi posílal serializovanou instanci třídy Foo$2, ale ten pod stejným názvem znal jinou anonymní třídu.

Ve třídě Foo navíc byly anonymní třídy použity při inicializaci fieldu (to je pro rekonstrukci chyby důležité – viz příklad níže), což není časté a zdůvodňuje to, proč se na problém nepřišlo dřív.

Nikde jsem se nedogooglil závazné specifikace, že anonymní třídy se mají číslovat $1, $2..., natož způsob určení pořadí dle jazykových konstruktů ve zdrojáku. (Nejpodobnější byla pouze tato stará chyba v JDT.) Pozorováním se zdá, že javac čísluje anonymní třídy podle jejich výskytu ve zdrojáku, zatímco Eclipse JDT projde nejdřív fieldy (možná tvoří implicitní konstruktor) a pak teprve metody:

Příklad

package numbering_anonymous_class;
public class Test {
    public void method() {new Error() {};}
    public final Object field = new Exception() {};
}

Pozn.: třídy Error a Exception jsou vybrány jen pro stručnost, aby nebylo nutné importovat ani překrývat nic v těle a byl poznat původ v Java decompileru (proto ne new Object() {}).

Po uložení v Eclipse a zkompilování dostává třída ve fieldu číslo $1, třída v metodě číslo $2:

c:\java\workspace\work\bin\numbering_anonymous_class>javap -c Test
Compiled from "Test.java"
public class numbering_anonymous_class.Test extends java.lang.Object{
public final java.lang.Object field;

public numbering_anonymous_class.Test();
  Code:
   0:   aload_0
   1:   invokespecial   #10; //Method java/lang/Object."<init>":()V
   4:   aload_0
   5:   new     #12; //class numbering_anonymous_class/Test$1
   8:   dup
   9:   aload_0
   10:  invokespecial   #14; //Method numbering_anonymous_class/Test$1."<init>":(Lnumbering_anonymous_class/Test;)V
   13:  putfield        #17; //Field field:Ljava/lang/Object;
   16:  return

public void method();
  Code:
   0:   new     #24; //class numbering_anonymous_class/Test$2
   3:   aload_0
   4:   invokespecial   #26; //Method numbering_anonymous_class/Test$2."<init>":(Lnumbering_anonymous_class/Test;)V
   7:   return

}

Po zkompilování v javac

c:\java\workspace\work\bin\numbering_anonymous_class>javac -d .. ..\..\src\numbering_anonymous_class\Test.java

dostává třída ve fieldu číslo $2, třída v metodě číslo $1:

c:\java\workspace\work\bin\numbering_anonymous_class>javap -c Test
Compiled from "Test.java"
public class numbering_anonymous_class.Test extends java.lang.Object{
public final java.lang.Object field;

public numbering_anonymous_class.Test();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   aload_0
   5:   new     #2; //class numbering_anonymous_class/Test$2
   8:   dup
   9:   aload_0
   10:  invokespecial   #3; //Method numbering_anonymous_class/Test$2."<init>":(Lnumbering_anonymous_class/Test;)V
   13:  putfield        #4; //Field field:Ljava/lang/Object;
   16:  return

public void method();
  Code:
   0:   new     #5; //class numbering_anonymous_class/Test$1
   3:   dup
   4:   aload_0
   5:   invokespecial   #6; //Method numbering_anonymous_class/Test$1."<init>":(Lnumbering_anonymous_class/Test;)V
   8:   pop
   9:   return

}

Poučení

  • Na pořadí anonymních tříd nelze spoléhat.
  • Všechno dělat jedním compilerem. (Nebo aspoň mít přehled, který compiler co dělá.)

Pokud s tím máte jiné nebo další zkušenosti, rád do článku doplním update.

9. 5. 2014

"Zápisky" z GeeCON 2014

Aby bylo jasno hned na začátku: na GeeCON 2014 letos nejedu. Omlouvám se za lstivé použití lacině lákavého nadpisu, když je teprve pár dní před konferencí. Ne, neumím cestovat v čase a netroufám si tvrdit, jaká tato konference letos bude. Přesto věřím, že bude skvělá a že kdo na ni pojede, nebude litovat! Nám se 15.4. narodilo miminko a po dobu šestinedělí jsem rozhodnut se od rodiny nevzdalovat. Není tak docela pravda, že přechod ze tří dětí na čtyři už je malý rozdíl, musíme najít nový režim fungování rodiny a ten by se z Krakowa nehledal úplně dobře :-).

Moji loňskou účast na konferenci sponzorovala společnost Y Soft, zápisky jsou na tomto blogu o pár příspěvků zpět. Mile mne překvapilo, že se mi letos Y Soft ozval sám od sebe znovu s volnou vstupenkou a o to více mrzelo, že musím odmítnout. Pak se mi na tom ale rozleželo pár věcí: (1) snaha se musí ocenit, i když ji nevyužiju, (2) neuskutečnili jsme lightning talk o loňském GeeCONu, takže stále cítím vůči Y Softu jistý závazek, (3) stejně na stránky http://2014.geecon.org/schedule občas zabrouzdám podívat se, o co přicházím. Výsledek: rozhodnutí nejet sice nezměním, ale to mi nebrání napsat o tom, co by mne nejvíc lákalo, kdybych jel.

Y Soft - platinový sponzor konference a mezinárodní společnost nabízející unikátní tiskové řešení (software i hardware), které umožňuje společnostem a organizacím efektivně kontrolovat náklady, snížit plýtvání, zvýšit komfort uživatelů a pozitivně působit na životní prostředí.

1. den


11.50: První jasnou volbu JDK8 rozmělňuje fakt, že přednášek na toto téma je poměrně dost i jinde. Nicméně očekávám, že letos bude track Oracle JDK8 už více praktický a více ovlivněn tím, že syntaxe a změny v standardní Javě už jsou na rozdíl od loňska zabetonovány. Takže by to mohlo být dobré. Jako rodič sedmiletého šachisty počítám i s možností, že by třeba mohl jednou syn přijít na chuť i programování, ale podle abstraktu prezentace Arona Gupty si říkám, že je jednak ještě čas, jednak se zdá, že vyžaduje něco vědět o Minecraftu a já jsem s počítačovými hrami poměrně na štíru. Anebo bych šel na AngularJS, kolem této technologie se pohybuji jen zpovzdálí a bylo by jistě zajímavé vidět ukázku nějakého živého usecasu.

13.40:  Buď streamy nebo Václav Pech. Streamy ze stejného důvodu viz předchozí odstavec, plusem by bylo, kdyby přednášející otevřel téma konstruktů jako Spliterator a Collector, o kterých se v tutoriálech moc nepíše (ve smyslu jak si správně napsat vlastní, tutoriály se koncentrují spíše na použití hotových). To se bohužel z abstraktu nedozvíme. A přestože jsem uživatelem Eclipse, Jetbrains mně stejně jako řadu dalších vývojářů imponují svojí schopností adaptovat rychle nové jazyky a vyznat se v DSL obecně.

14.50: Pravděpodobně Mutation Analysis, zajímavé téma, aktuální i v prostředí našich testů. Případně JavaFX (mám dojem, že tato technologie prochází jakýmsi latentním bojem za to, že ještě není mrtvá - osobně si tím nejsem jistý, ale protože děláme aplikaci ve Swingu, máme k ní pořád ještě blízko). Případně REST, kvůli inspiraci, zda by nešlo dělat něco lépe v SOAP hellu našich webservis :-).

16.10: Tady buď How to Participate in the Future of Java (v jednom z předchozích blogpostů jsem žehral na nesrozumitelnost JCP) nebo Kirk Pepperdine (známý speaker a aktuální téma, pořád se točíme kolem Java 8). Zajímavě vypadá ale i The social developer - netechnické téma slibuje zpestření a jako vývojář cítím, že je třeba usilovat o všestrannost.

17.20: HTML5, ze zájmu. Webové technologie mi teď trochu utíkají, tak tomu pomoci to dohnat.

2. den


9.00: Redesign of Legacy Frameworks. Nakládání s legacy kódem (a bohužel i jeho vytváření, protože nestíhám psát testy v takovém rozsahu, jak bych si představoval) je u nás denní chleba a legacy framework je o to horší tím, že je více prorostlý do aplikace. Může to být zajímavá zkušenost. Taky Everything ... Is Wrong vypadá podle abstraktu zajímavě a může to být velmi praktické. Začátečníkům v Javě bych asi doporučil spíš tuto přednášku, může rozšířit obzor díky uvedení historických kontextů jednotlivých rozhodnutí ve vývoji Javy.

10.20: Jasná volba Lukas Eder a jeho JOOQ framework - jednoduchá myšlenka, geniální alternativa k ORM. Na druhém místě by to byl Peter Lawrey, ale na rovinu přiznávám, že tady by to bylo spíš kvůli osobě přednášejícího, neboť jeho blog VanillaJava mně i po 11 letech praxe ukazuje stále nová překvapení. Věděli jste například, že metodu vracející pole intů lze deklarovat i jako public static int method()[] {...}? (ano, hranaté závorky jsou mezi parametry a blokem těla metody, analogicky ke způsobu deklarace proměnné int x[];) A přeloží to i Eclipse.

11.40: Advanced Spock, loňská přednáška o Spocku od stejného přednášejícího byla dobrá.

13.30: Pravděpodobně TDD, mám podobně skeptický postoj - v našem prostředí si neumím představit psát testy dopředu vzhledem k tlaku na dodání. Nebo se hodit do vody na nějaké paralelní přednášce (témata, s nimiž nemám zkušenosti).

14.40: Buď technickou přednášku CompletableFuture (za účelem dostat se do Javy 8 z Guavy, kde jsem už použil ListenableFuture), nebo Seven Ineffective Coding Habits of Many Java Programmers - "okecávací", ale nevěřím, že by mi nepromluvila do duše.

16.00 (LT): Custom Assertions nebo Life is too short, ale spíš ta první, jsem přece jen víc praktik.

16.20 (LT): Tematicky blízko k Reverse engineering, ale protože se pořád co se týká návštěv konferencí považuji za začátečníka, asi bych neodolal Be the perfect attendee.

16.40 (LT): Pair programming je zajímavé téma, ale asi bych dal přednost Chromu. Vybavilo mi to vzpomínku na Firefox plugin Ubiquity, zajímalo by mě, jak by se taková věc udělala v Chromu.

3. den


9.00: Jasná volba Functional Programming Without Lambdas. Lukas Eder a tato přednáška by pro mne byly asi nejlákavější body konference. Jsem si jist, že v přednášce bych se se zkušenostmi s Guavou našel. A myslím, že pokud na ní bude dost programátorů zvnějšku nucených dělat na Javě < 8, bude z ní obecný povděk případně zajímavá diskuse.

10.20: Buď Java8 pro zařízení - poznat něco nového, nebo 33 things, s čímž se mi zas asociuje "poznat známé věci z jiného úhlu". Pro druhou možnost mluví to, že spoustu zmíněných technologií používáme a taky si pamatuji přednášejícího z výborné přednášky "SOAP sucks" z loňska.

11.40: JavaFX z podobných důvodů jako první den, nebo Spring Testing. Zajímavé téma je i Asciidoctor a myšlenka Asciidocu, osobně formáty tohoto typu považuji za mnohem intuitivnější než např. XML (ztělesněné v Docbooku), HTML nebo LaTeX.

13.30: Těžké rozhodování mezi Sandrem Mancusem a Nikitou Salnikovem. Sandro Mancuso měl z mého pohledu nejlepší přednášku loňského GeeCONu o refactoringu, nyní ve svém abstraktu slibuje nahlédnutí do návrhu jeho poslední aplikace z pohledu základních principů a praktik OOP. Očekávám srozumitelný a od reality neodtržený výklad. Oproti tomu Nikita Salnikov ve své přednášce cílí na jeden konkrétní fenomén, který je ovšem pro aplikace kritický - memory leaky. Osm různých důvodů vzniku OutOfMemoryError - PermGen space (pomiňme nyní, že permgen je v JDK8 již odstraněn) bych tedy vyjmenovat nedokázal a tato přednáška je anotována popisem "1h live coding", takže o praktický přínos se neobávám.

14.40: So you want to write another JVM language? Ne že bych ho chtěl psát, ale vypadá to na pěkně komplexní vhled do této problematiky, a kromě toho mne na abstraktu láká poslední věta: dokáže to (napsat vlastní jazyk pod JVM) i ten, kdo nestudoval computer science a teorii překladačů a většinu života nevěděl, co je AST. Což jsem dělal poslední roky na FELu a v diplomce.

Závěr


GeeCON letos nabízí nadupaný program, z nějž je co vybírat. Není opomenuté žádné ze žhavých témat poslední doby a stojí za ocenění, že oproti loňsku organizátoři zvýšili počet tracků na 5 a taky je tématicky pojmenovali.

Rád bych zdůraznil, že výběr či nevýběr prezentací vychází z mých osobních preferencí ovlivněných mým profesním pozadím. Nechci, aby neuvedení některé prezentace nebo skepse zmíněná v kontextu mé situace byly chápány tak, že ji nedoporučuji nebo apriori považuji za nekvalitní pro všechny. Např. vzhledem k situaci našeho týmu a projektu jsem moc neřešil přednášky o agilní metodice, vzhledem k podmínkám určovaným zákazníkem neřeším přednášky o cloudu nebo UX, ale vím, že kolega ve vedlejším týmu by k tomu přistupoval opačně. Z důvodu, že volba je jasná, nechci ani komentovat prezentace spojené přes všechny tracky.

Uvítám samozřejmě i upozornění na potenciálně dobrou přednášku v diskusi pod článkem. Tak si to pěkně užijte a doufejme na viděnou na GeeCON 2015!


20. 3. 2014

Nezvyklá příčina NoClassDefFoundError

Zápisky z troubleshootingu zapeklitého problému, který mne stál den hledání. Aplikace (war v Tomcatu) vyhazovala NoClassDefFoundError na třídy, které evidentně ve waru jsou (byly buď přímo v classes, nebo v jarech v lib). Moc nesetříděno a nezaručuji, že nejsou zavádějící.

Příčina


V systému došlo souběžně k jiné chybě - vyčerpání maximálního počtu povolených souborů (včetně síťových a db připojení). To jsme sice věděli (chyby jako java.net.SocketException: Too many open files nebo java.io.FileNotFoundException: /soubor (Too many open files)
byly diagnostikovány a bylo nutné zkontrolovat potenciální resource leak a limit nechat navýšit (ulimit -n, cat /proc/5063/limits). Nenapadla mne ale spojitost s NCDFE. Pokud JVM při pokusu o nahrání třídy narazí na maximální počet otevřených souborů, dostane se zřejmě do nějak nekonzistentního stavu, kdy v classloaderu se třída jeví jako nahraná, ale pokus o přístup na ni končí NCDFE. Bližší rozbor jsem nikde nevygooglil, ale nejvíc se mu blíží tato otázka na SO, první stopa vedla z tohoto dotazu.

Řešením byl restart Tomcatu, ale bylo dobré zjistit příčinu.

Falešné stopy


Zkontrolovat filesystem


V minulosti se nám stalo, že na předprodukčním serveru (virtualizovaný server s Windows Server 2003 - dnes už je upgradován) došlo k poruše, která se projevila náhodným porušením náhodného souboru. Stalo se to u jaru z instalace Javy, kde rozdíl byl pouze o 1 bit! V dnešním případě šlo o produkční prostředí a Linux a tehdejší chyba se taky projevovala myslím jako InternalError, nicméně jsem zkontroloval, že jary jdou rozzipovat a že hlášené classy jsou validní (pro jistotu jsem se na ně podíval i přes oblíbený decompiler).

Hierarchie classloaderů byla v pořádku


Klasická tomcatí: všechny aplikančí třídy z waru jsou nahrány WebappClassLoaderem.
Odkazy: 1, 2, 3 - pěkný tutoriál, i když k IBM JVM


Nesnažit se o odstranění třídy z classloaderu


Nejde to. Je to dáno jednak prioritou classloaderů, jednak tím, že příslušnost classy v classloaderu je na úrovni nativního kódu. Nepomůže ani vlamovat se do classloaderu pomocí reflexe (Bad.class.getClassLoader().resourceEntries.remove("com.pkg.Bad")) ani snažit se třídu nahrát pomocí defineClass (protože už tam je a hodí to LinkageError: loader ... : attempted  duplicate class definition), ani totéž rodičovským classloaderem  (Bad.class.getClassLoader().resolveClass(Bad.class.getClassLoader().getParent().defineClass("com.pkg.Bad",new byte[] {...}),0,length))), protože by se tak musely zavléct všechny třídy, na kterých nahrávaná třída závisí.
Odkazy: 1, 2, 3, 4.


Třída šla instancovat z konzole


Na projektu je Beanshell konzole, v nímž šlo vytvořit přímo instanci třídy. Nešlo ale vytvořit factory a na ní zavolat metodu pro vytvoření třídy - to házelo NCDFE. Nezkoumal jsem dál, asi to je prostředím Beanshell interpreteru.

Nemá cenu zjišťovat všechny nahrané třídy


Reflexní triky, jak zjistit všechny nahrané třídy (viz např. zde) jsou založeny na zkoumání výsledku metody Class.getProtectionDomain() a ProtectionDomain.getCodeSource() a tady nepomáhají, protože dotčené třídy (pro které se vyhazuje NCDFE) mají tyto údaje i classloader v naprostém pořádku.


Class loading


Do JAVA_OPTS při spouštění aplikačního serveru jsem přidal -verbose:class. Těch pár tisíc řádků navíc v catalina.out ničemu nevadí a třeba informace o tom, zda třída byla nahraná, příště pomůže. Později jsem ještě objevil SO otázku s dalšími experimentálními přepínači.

Užitečné odkazy

O classloaderech na oracle.com



17. 3. 2014

Google Guava – bilancování v předvečer releasu Javy 8

Tento článek záměrně načasovávám na dobu, kdy vychází release GA Javy 8. Pozoruji, jak se už cca od Vánoc na internetu začínají množit články typu "Lambda funkce snadno a rychle" nebo "X nových věcí v Javě 8" (oblíbený formát dzone.com) a počítám s tím, že po 18.3.2014 bude svět Java vývojářů tímto tématem ještě více zaplaven. Proč jsem si tedy vymyslel, že zrovna teď napíšu o Guavě? Není tato knihovna utilit s příchodem Javy 8 už mrtvá nebo alespoň za zenitem?

Následující zamyšlení možná pomůže se odpovědi na tuto otázku přiblížit. Guavu aktivně používám od verze 7, která byla vydána roku 2011. Vyzkoušel jsem na našem projektu velkou část funkcionality, kterou nabízí. Odebírám maily z issue trackeru. Navrhl jsem jeden malý enhancement, který byl akceptován. Uspořádal jsem v našem týmu interní školení se zaměřením na to, jak naše dosud používané patterny v kódu psát efektivněji pomocí Guavy. Výrazně mi usnadnila myšlenkový přechod z objektově-imperativního paradigmatu na objektově-funkcionální. Rád bych zde shrnul svůj pohled na její hlavní přednosti a slabiny a na to, jak je případně ovlivní nástup Javy 8.

Co se mi na Guavě líbí

Nabízí nové datové struktury

Každý, kdo navrhuje nějaký algoritmus, dobře ví, jak důležité je zvolit správné a efektivní datové struktury a Guava tým v tomto jistě není výjimkou. Doba, kdy jediný způsob, jak reprezentovat neskalární data, je seznam, je doufám nenávratně pryč a když mi ji někdo připomene např. kódem, který namísto použití Set prochází v O(n) seznam, zda v něm neexistuje prvek, který má v úmyslu vložit, vytahám ho za uši. Pokud je pro vás samozřejmé vědět o asymptotické složitosti základních kolekcí, pokud máte v hlavě zažité rozhodování á la např. tento diagram, pokud často potřebujete struktury jako Map<Klíč,Set<Hodnota>> nebo máte představu, k čemu je dobrá mapa s unikátními hodnotami, pak si v packagi com.google.common.collect přijdete na své a multisety, bimapy, tabulky a jiné se stanou cenným pomocníkem ve vaší práci.

Je škoda, že takové třídy nebyly přidány do Javy 8, v níž by byly ještě užitečnější díky streamům.

Minimalizuje objem pomocného kódu na projektu

Nepočítám-li projekty, u kterých je neexistence takového kódu cílená a zdůvodněná, asi bychom v běžném životě těžko hledali projekt, který by neměl alespoň jednu třídu Utils, Helper, apod. V takové třídě se shromažďuje vše, co je příliš obecné v porovnání s doménou řešeného problému, co se často opakuje (v horším případě u čeho vývojář podlehne klamnému dojmu, že se to bude často opakovat :-)) nebo prostě která se jinam nevešla. Napsat takové třídy může být lákavé pro někoho, kdo si chce zaprogramovat, ale představuje to zátěž pro údržbu a jako komponenta s vysokou mírou znovupoužití přináší zvýšené nároky na kvalitu a zpětnou kompatibilitu. Použitím Guavy se značně zvýší pravděpodobnost, že tyto utility nebude nutné psát resp. že volání stávajícího proprietárního kódu půjde poměrně snadno refactorovat na volání Guavy.

Promyšlený kontrakt utility metod


Na API všech tříd Guavy je vidět, že ho navrhoval někdo, kdo jej opravdu používá a ví, že kód se jednou píše, ale mnohokrát čte. Je patrné, že řešený problém je důkladně zanalyzován a oproti např. Apache commons je mnohem důsledněji aplikováno objektové paradigma. Klasickým příkladem je třída Splitter, která rozlišuje vlastnosti procesu dělení řetězce (metody splitteru) a samotný vstup (parametry metody split), v protikladu ke skoro kombinatorické explozi čtrnácti nejrůznějších přetížení metod začínajících split... ve třídě StringUtils v Apache commons. Podobně ze srovnání vyjde CharMatcher a metody StringUtils.is.... Přetěžování metod se přitom v Guavě nebojí – když uznají za vhodné, udělají klidně 13 přetížení ImmutableList.of a 6 přetížení ImmutableMap.of. Tato čísla nejsou od stolu vystřelené Pišvejcovy konstanty, ale jsou podložena statistickými měřeními v codebase Googlu. Hojně se používá fluent API (řetězení metod). Názvy metod jsou akční, s důrazem na maximálně vypovídající popis činnosti metody (srovnej např. pojmenování Strings.nullToEmpty s ekvivalentním StringUtils.defaultString v Apache commons). To mi mimochodem připomíná, že až na název metody of a pár dalších specifických míst se dá ve většině případů použít statický import metody, a to bez velké újmy na čitelnosti.

Tento přístup je příkladný i pro práci na vlastním projektu. Jeho nejčastějšími opaky, kterých bývám svědkem, jsou: nedostatek úsilí věnovaného výstižnému pojmenování, použití příliš abstraktních termínů náchylných k tomu, že si pod nimi každý představí něco jiného, a děravý návrh meziobjektové komunikace (přílišná závislost na interním stavu, tichý předpoklad volání metod v určitém pořadí nebo volání metody předka apod.). Podívejte se např. na diskusi (včetně Google+), která se vedla o to, zda se u stopek pro měření času má nahradit volání new Stopwatch().start() za factory metodu Stopwatch.createStarted(), protože je tak z názvu víc zřejmé, zda jsou stopky spuštěné a eliminuje se nejasnost, v jakém stavu jsou po skončení konstruktoru. Kolik programátorů by nad tím mávlo rukou?

Oracle si je u Javy jistě vědom této nutnosti udělat API co nejpříjemnější a nejlogičtější. Situaci docela vystihl Marcus Lagergren – speaker z Oraclu na Geeconu 2013, kterého jsem se na toto téma – zda si Java 8 vezme v tomto inspiraci z úspěšných opensource projektů – ptal, a jehož odpověď zněla "We inspired from everything." Pokud se mám držet výše uvedených ukázek, v Javě 8 teď nacházíme jak jednoduché metody jako String.join nebo Objects.toString připomínající stavbou a pojmenováním Apache commons, tak složitější (byť oproti Guavě méně všestranné) konstrukce jako StringJoiner. Dlužno připomenout, že Java 8 měla pro přidání metod o jednu motivaci navíc: použití jako method reference. Je taky férové dodat, že je dobře, že se Oracle nesoustředil na tyto titěrnosti, ale na věci podstatné a principielní. Takže máme Optional, které nahrazuje guavácké Optional, CompletableFuture zhruba nahrazující guavácké ListenableFuture a především streamy. Při zamhouření oka si lze představit, že aplikace streamů na výsledek String.split nebo použití Pattern.splitAsStream bude srovnatelně čitelné s použitím guaváckého Splitteru.

Realistický přístup k funkcionálnímu programování

Funkcionální paradigma za poslední roky prokázalo, že si zaslouží návrat na scénu. Dokládá to množství nových funkcionálních jazyků a funkcionálních rysů do existujících jazyků včetně Javy 8. Nechci zde rozvíjet úvahy, zda to je či není pozdě. V duchu výroku "Opravdový programátor umí psát FORTRANské programy v kterémkoliv jazyce." ze slavného článku z roku 1983 je spíš zajímavé sledovat, jak se dá funkcionálně programovat v Javě před verzí 8. Nic moc, že? Podpora v jazyce vesměs žádná, zápis anonymních tříd zdlouhavý. Tak si něco napíšu, řekne si autor budoucí knihovny. Tady je právě ta past: tato oblast není zas až tak těžká na naprogramování – napsat interface Predicate nebo Function a kód, který pomocí nich prochází generickou kolekci a transformuje/filtruje/redukuje její prvky, s trochou zkušenosti celkem jde. To, co snahu o překlenutí absence funkcionálních rysů dělá úspěšnou, jsou zde více než jinde netechnické aspekty. Nestačí dát jen nástroj, ale i jakousi filozofii, jak s ním mají uživatelé nakládat a přitom si zachovat zdravý odstup s vědomím, že to, co používají, je v podstatě jen workaround, protože samotný jazyk nám, co si máme povídat, nepomáhá.

Tím si vysvětluji, proč se vyrojilo tolik knihoven pro podporu funkcionálního programování v Javě, z nichž většina je téměř neznámá: Apache Commons, Functional Java, FunctionalJ, LambdaJ, Jambda, Totally lazy, Bolts, Fun4J, nadstavba nad Guavou Fugue... Nechci, aby následující text vyzněl cynicky, vážím si práce všech, kteří produkují opensource software, ale většina knihoven nabízí v podstatě jen vlastní funkcionální interfacy (které se mezi knihovnami liší jen pojmenováním) a nad nimi nějakou sadu utility metod. Ta jde mimochodem někdy pěkně do šířky (osobně si neumím představit, kdy by se mi vyplatilo např. použít toto místo klasické konstrukce switch). Známější knihovny pak použivají chytřejší triky, jejichž výhod i úskalí jsou si předpokládám jejich uživatelé vědomi: proxy objekty (LambdaJ) nebo preprocessing nějaké čitelnější syntaxe.

Filozofie, kterou Guava ve funkcionálním programování razí, je: "dobře, tady je funkcionální podpora, ale je to nutné zlo, jazyk na to stejně není vybaven", případně "nepřeplácejte to s anonymními třídami, nesnažte se o one-liner za každou cenu" (slušné znění). V issue trackeru resp. na StackOverflow lze najít zoufalé příspěvky typu "jak se mám podrbat levou nohou za pravým uchem" – rozuměj jak zřetězit šňůru anonymních predikátů a funkcí k dosažení požadované transformace dat – následované trpělivou reakcí někoho z Guava týmu typu "použij obyčejný for/if". Přiznávám, že jsem si tímto obdobím taky prošel, vyléčil se z něj a o to víc přístup Guavy vnímám jako pokorný a pravdivý.

Java 8 tento realistický přístup zachovává, vždyť tělo lambda funkce může být imperativní blok kódu. Je tedy trochu paradoxem, že ten, kdo dosud programoval imperativně nebo pouze pomocí anonymních tříd, bude mít z přechodu větší dojem zlepšení než ten, kdo využíval složitější konstrukce. Např. pro výběr všech osob ve věku 36 let je přechod z anonymního predikátu

Iterables.filter(personSet, new Predicate<Person>() {
    public boolean apply(Person p) {
        return p.getAge() == 36; 
    } 
}

na lambda funkci

personSet.stream().filter(p -> p.getAge() == 36)

poměrně značná úspora, zatímco při realizaci pomocí

Function<Person,Integer> ageFunction = new Function<Person,Integer>() {
    public Integer apply(Person p) {
        return p.getAge(); 
    } 
}
Iterables.filter(personSet, Predicates.compose(Predicates.equalTo(30),Person.ageFunction));

jen ušetříme funkci díky referenci na metodu, ale vlastní kód se o tolik nezjednoduší:

personSet.stream().filter(Predicates.compose(Predicates.isEqual(30),Person::getAge));

Jsem velmi zvědavý, jaký bude další život funkcionálních rysů v Guavě. Určitě tam ještě nějakou dobu budou – cílovou skupinou současné Guavy jsou uživatelé Javy 5 až 7. Nicméně nedivil bych se, kdyby je jednou zcela vyhodili. Díky tomu, že lambda funkce je kompatibilní s každým funkcionálním interfacem, s jehož jedinou abstraktní metodou se shoduje v typech parametrů a návratové hodnoty, by tento přechod mohl být celkem pohodlný pro uživatele nejen Guavy, ale všech ostatních výše vyjmenovaných funkcionálních knihoven.

Symptomy živého opensource projektu

Guava je knihovna Googlu, není proto překvapením, že je hostována na Googlecode a ne třeba na Githubu. Na oficiální site projektu je vidět, že nejsou zanedbány žádné vlastnosti dobrého opensource projektu. Cenným zdrojem informací je wiki návod – soubor stránek s názvem končícím ...Explained. Jednoduchý issue tracker se srozumitelnými stavy issues, vyznačením, zda je příspěvek od člena Guava týmu, a flexibilním avšak nepřeplácaným vyhledávačem. Pozitivní přijetí komunitou, komunikace přes StackOverflow i Google+.

Nechci se v tomto ohledu pouštět do porovnávání Guavy a Javy, jedna knihovna a celá platforma jsou zkrátka nesrovnatelné. Přesto mi přijde, že porovnávat s Javou knihovnu typu Guava je přece jen o malinko menší blbost než jinou knihovnu. Guava cílí na to být obecně (znovu)použitelná a univerzální. Neuškodí jí ani to, že je spojovaná s velkou společností (mám pochopení, pokud to někomu dává dobrý pocit, ale reálně nemám tušení, do jaké míry je to v Googlu aktivita nadšenců a do jaké míry to má politickou podporu). Java jako platforma má samozřejmě taky zvládnuté věci vyjmenované v předchozím odstavci, je to ale přece jen větší kolos. Zatímco nástroje typu Googlecode a způsob, jakým je dobré opensource projekty využívají, jsou pro mne intuitivní a rychle pochopitelné, v protikladu k tomu musím přiznat, že procesu JCP a způsobu, jakým se tvoří a vyřizují JSR a JEP, rozumím jen velmi povrchně. Nepůsobí na mne "nízkoprahově", nemám z něj dojem otevřených dveří.

Pružné zacházení s novými nebo odstavenými featurami

"Compatibility matters" – toto motto si pamatujeme ještě z doby Sunu. Bylo jedním z příčin úspěchu Javy, protože sázelo na lidskou nechuť měnit něco, co funguje. Díky němu si i dnes mohu být teoreticky jistý, že mi můj úžasný program, který projde data v Dictionary, zobrazí je pomocí GridBagLayoutu a pak se ukončí pomocí Thread.stop(), stále poběží. (Čtenář jistě pochopí moji lenost doplňovat k těmto třídám živé odkazy :-)). Guava tuto zátěž vytvářet nechce. Deprecované API (když už nějaké je) je odstraněno 18 měsíců od označení @Deprecated. Neustálené nebo experimentální API je označeno anotací @Beta, která představuje jakousi výstrahu "používat na vlastní nebezpečí". Je vidět, že Guava vznikla v prostředí, kde kód hodně žije, a proto i od projektů, které ji používají, očekává jistou tvárnost. Specifickým fenoménem, který do tohoto bodu řadím, je hřbitov nápadů – seznam featur, o kterých se autoři zařekli, že tam nikdy nebudou a přes to nejede vlak.

I když ani v tomto aspektu nelze Guavu s Javou porovnávat, vnímám pozitivně jak dynamičnost Guavy, tak konzervativní postoj Javy.

Co mi na Guavě vadí

Funkcionálních rysů mohlo být přece jen víc

Ne zas o moc, abych si neprotiřečil výše napsané oslavné ódě :-). Ale často postrádám vše, co souvisí s prací s dvojicemi: Pair, operace fold, BiFunction, případně transformace známá z Pythonu pod názvem zip. Také FluentIterable (analogie sekvenčního streamu z Javy 8) bylo přidáno až ve verzi 12, přičemž ostatní fluent kolekce a mapy zatím nejsou a je docela možné, že teď už se s nimi dělat nebudou. Totéž platí zřejmě i pro nedostatek pomocných funkcí a predikátů, např. by se hodilo cosi jako Collections.sizeFunction nebo Strings.lengthFunction. To ale Java 8 aspoň vyřešila mechanismem "method references".

Open source verze je podmnožina interní knihovny Googlu


Tento bod nepatří v pravém smyslu do výčtu negativ. Pokud chcete používat Guavu, počítejte s tím, že v issue trackeru můžete narazit na řadu připomínek, na které Guava tým reaguje: "jo, to už používáme interně, možná to někdy releasnem", takže aktuálně je jediná možnost si to sám naprogramovat. O důvodech můžeme asi jen spekulovat, ale myslím, že nejčastějšími důvody jsou nevyjasněnost, zda požadavek zapadá do filozofie Guavy, a nedostatek statistických měření z codebase.

Případně (mám ten dojem např. odtud – všimněte si datumu) i strategicko-obchodní důvody a snaha nedělat knihovnu zas úplně dokonalou – Google je komerční firma a nic opensourcovat nemusel; pokud tak udělal, jsem za to jedině rád a rozhodně tento bod nemíním jako výtku.

Odmítání a váhavost u některých požadavků

O přidání nějaké featury do Guavy se rozhoduje podle principu utility times ubiquity. To znamená, že featura má šanci se do Guavy dostat, pokud je v porovnání s implementací bez Guavy významným přínosem v oblasti množství napsaného kódu, čitelnosti nebo výkonu (utility) a pokud pro ni existuje množství use casů (ubiquity). Zaručený způsob, jak dostat zamítnutí požadavku, je napsat, že by to v Guavě mělo být jen tak pro úplnost. Na spoustě požadavcích v issue trackeru je sice vidět, že mnoho uživatelů o tomto pravidlu neví a dostávají reakci zaslouženě, nicméně v některých případech ho Guava tým dodržuje až příliš křečovitě.

Příkladem je třeba chybějící přetížení metody Iterables.getFirst() bez defaultní hodnoty (oficiálně doporučeným workaroundem je prosté volání iterator().next()), zatímco Iterables.getLast() má přetížení s defaultní hodnotou i bez ní. Domnívám se, že ustoupit z principu ortogonality ve prospěch principu jednotnosti a abstrakce by v tomto případě bylo lepší.

Dalším jinak správným postojem, který bývá v případě Guavy doveden do extrému, je dlouhé promýšlení nového požadavku. Uživatel pošle návrh na přidání funkcionality, někdo z Guava týmu mu odpoví, že je to specifický případ něčeho obecnějšího, issue se přepne do stavu Research a začne běžet čas. K dnešnímu dni to je 190 požadavků, přičemž polovina z nich je starší dvou let. Jak jsem psal v minulém bodu, stavím se ke Guavě s respektem a vděčností. Rozhodně si nepředstavuji, že se celou tu dobu tým radí, jak vyvinout něco úplně dokonalého, mají své práce dost. Je potřeba s tím prostě jen počítat. Dlužno dodat, že v případě, že se dočkám, to pak stojí za to.

Nejvíce mi chybí přidání fluent kolekcí a map, kde mi na současném stavu vadí nepřehlednost, s jakou se do sebe vnořují volání Maps.transformValues(Maps.filterValues(map,predicate),function). Domnívám se, že přechodem na fluent zápis by zde byl princip utility times ubiquity krásně splněn. Podobných případů se ale mezi požadavky ve stavu Research dá najít víc.

O filozofii Guavy taky vypovídá věta "když to nepotřebuje nikdo v Googlu, potřebuje to vůbec někdo?". I když hlavním kritériem pro ubiquity jsou měření na codebase Googlu, při reakci na nový požadavek od někoho zvenčí se Guava tým vždy silně zajímá o realistický, praktický use case. V naprosté většině případů se jedná o dobrou radu od zkušeného programátora, ale je potřeba mít na paměti, že co je dobré pro Google, nemusí být dobré i pro mne.

Každý bod končí přenesením příslušné úvahy na Javu 8 a zde to bude triviální: na ni jsme čekali dlouho a teď jsme se dočkali :-).

Monolitický jar

Guava se distribuuje jako jeden jar, který v aktuální verzi 16 má 2.1 MB. Pro řadu uživatelů už to je moc, nicméně oficiálně doporučovaný workaround je poměrně nestandardní – použití nástroje ProGuard. Na našem projektu je součástí klientské aplikace stahované přes Java Web Start. Vzhledem k ostatním okolnostem je to zatím únosné, přesto se moc netěším na dobu, kdy nastane potřeba tento stav změnit. Bude to pak zřejmě rozhodování, zda se pustit do konfigurace ProGuardu a zásahu do build procesu anebo – alternativně – zda nesáhnout po open source projektu Seeds, což je Guava rozdělená do několika jarů (funkčně shodné, pouze je změněn název hlavního package) dobrovolníkem Jesusem Zazuetou, kterému zřejmě došla trpělivost a udělal analýzu závislostí a rozdělení na submoduly sám (odkazy na nové porty Seeds pro nové releasy Guavy posílá jako komentáře do issue trackeru).

Návod na použití ProGuardu je poměrně vágní, což mi moc nesedí dohromady s precizností ostatních návodů. Vzhledem k tomu, že končí větou "We would like to hear about your experiences using ProGuard with Guava so we can improve this page.", poslední editace stránky je z května 2012 a uživatelská komunita je jinak poměrně živá, vyvozuji z toho, že komunita tento bod filozofie Guavy nepřijímá za vlastní a podobně jako my to vždycky radši nějak překousnou s velkým jarem.


Touto poslední připomínkou ke Guavě se zároveň loučím i s Javou 8, protože očekávám, že popisovaný problém bude vyřešen v Javě 9 – projektu Jigsaw. Ale o tom si povíme zas za 2 roky :-).

Závěr

Domnívám se, že Guava bude mít stále smysl pro Javu 5 až 7, ale v zeštíhlené podobě i pro Javu 8. Guava je mi dobrou pomůckou při plnění programátorských úkolů, kde je nežádoucí přílišné odvádění pozornosti k implementačním detailům. V článku jsem se snažil o prezentaci pragmatického postoje – nejsem fanboy, nenosím ji na tričku a pokud čas ukáže, že je za ni lepší náhrada, nemám nejmenší problém ji postupně opustit. To však nesnižuje uznání, které bych chtěl vyjádřit autorům za to, že taková knihovna existuje a dali ji komunitě k dispozici.

Máte s Guavou taky vyhraněnou zkušenost? Setkali jste se v Guavě s dalšími příklady fenoménů, které jsem popisoval? Budu rád, když se podělíte v komentářích.


4. 2. 2014

Small Eclipse breakpoint pitfall


I was caught in interesting trap in Eclipse today. It was my mistake in the beginning, however I suppose Eclipse could have better handled this situation.

Look at methods m1() and m2() in following minimalistic example. What's the difference between them?

public class StrangeBreakpoint {

  public static void main(String[] args) {
    m1();
    m2();
  }

  private static void m1() {   // line 8
    int x = 0;
    while (true) {             // line 10
      System.out.println(x++);
      if (x == 2) break;
      int dummy = 0;           // line 13
    }
  }
 
  private static void m2() {   // line 17
    int x = 0;
    while (true) {             // line 19
      System.out.println(x++);
      if (x == 2) break;
    }
  }

}

Certainly none in output. But try to enable breakpoints at lines 10 and 19. Method m1() runs loop once before stopping at breakpoint, method m2() never stops at breakpoint at all.

I guess the reason of m1()'s behavior is the additional code after break which results in additional bytecode delimited with jumps. Eclipse debugger treats breakpoint as stopping point on first instruction after break (see instruction offset 20 paired with line 13). In m2() case, the debugger has no instruction to stop at, so it silently ignores breakpoint.

>javap -private -c -l StrangeBreakpoint
Compiled from "StrangeBreakpoint.java"
public class StrangeBreakpoint extends java.lang.Object{

...

private static void m1();
  Code:
   0:   iconst_0
   1:   istore_0
   2:   getstatic       #24; //Field java/lang/System.out:Ljava/io/PrintStream;
   5:   iload_0
   6:   iinc    0, 1
   9:   invokevirtual   #30; //Method java/io/PrintStream.println:(I)V
   12:  iload_0
   13:  iconst_2
   14:  if_icmpne       20
   17:  goto    25
   20:  iconst_0
   21:  istore_1
   22:  goto    2
   25:  return

  LineNumberTable:
   line 9: 0
   line 11: 2
   line 12: 12
   line 13: 20
   line 10: 22
   line 15: 25

...

private static void m2();
  Code:
   0:   iconst_0
   1:   istore_0
   2:   getstatic       #24; //Field java/lang/System.out:Ljava/io/PrintStream;
   5:   iload_0
   6:   iinc    0, 1
   9:   invokevirtual   #30; //Method java/io/PrintStream.println:(I)V
   12:  iload_0
   13:  iconst_2
   14:  if_icmpne       2
   17:  return

  LineNumberTable:
   line 18: 0
   line 20: 2
   line 21: 12
   line 23: 17

...
}

I ran into this issue debugging real application, not noticing first loop run and wondering why the value of x seems different than the one just assigned.

This behaviour was reproduced in Eclipse Juno, JDT 3.7.101.v20120725. Intellij Idea disallowed breakpoint in both cases, which I consider more fair and less surprising.

Moral: If you must use breakpoints at all, be aware of bytecode in the background. Avoid usage in places where semantic gap between high-level language and bytecode is not apparent.