10. prosince 2008

Překlad výjímek z databáze při použití Hibernate

Kdo používá Hibernate, ten možná již tuší, kam budu dnešním příspěvkem mířit. Jde o to, že chyby, které detekuje Hibernate se snaží do výjimky dávat jméno třídy a atributu, který chybu vyvolal. Ovšem pokud vám chybu vyhodí databáze, pak vás Hibernate nechá ve štichu a předhodí vám výjimku datábáze a dělejte si s ní co chcete.

Toto chování se mi nikdy nelíbilo, protože mu moc nerozumím, Hibernate má vše co potřebuje. Zná dialekt, který by umožnil překlad databázově závislých výjimek, do obecné formy obsahující jméno tabulky a názvy sloupců. Následně je možné využít mapování a přeložit název tabulky a názvy sloupců na jméno třídy a atributů. Proč to Hibernate nedělá? Nevím ...

My jsme vždy chtěli mít vlastní DAO výjimky, které by byly nezávislé na použitém ORM. A zároveň jsme chtěli aby obsahovaly jméno třídy a atributů. Nikoliv tabulky a sloupce, protože tyto výjimky jsou většinou zpracovávány vrstvami aplikace, které o databázi nemají mít nejmenší tušení.

Řešení jsme měli, ale nelíbilo se nám, protože jsme de facto museli mapování zapisovat dvakrát (jednou z objektů do relací pro Hibernate a podruhé z relací do objektů). Dodnes nevím proč nejsou programátorsky přístupné mapovací soubory, nebo nevím jak je z Hibernate dostat, což jsme chtěli pro překlad sloupců na atributy použít. Občas jsme diskutovali co s tím, ale ne a ne přijít na ten správný nápad. Až takhle jednou jdeme z oběda a s kolegou Lubošem jsme si dali brain storming a triviální řešení jsme našli (nechápu jak to, že nás nenapadlo dřív).

Je až trapně triviální. Stačí si rozparsovat Hibernate mapovací soubory a vytvořit z nich mapování opačné, než jaké reprezentují. Tato akce představuje nesložitý algoritmus, jedinou složitostí je dědičnost, kde je nutno vytvořit vhodnou reprezentaci, aby se při zpětném překladu zvolilo správné jméno třídy (to nejkonkrétnější), v případě překladu více atributů umístěných různě hluboko v hierarchii tříd.

Druhým krokem je vytažení informace z SQLException, kterou vyhodí databáze. Zde je pár drobných oříšků, např. získání jmen sloupců v případě chyby unikátního klíče (námi použitá DB2 vrátí jméno unikátního klíče, které je nutno pomocí katalogu překlopit na jména sloupců).

Takže cca. po 3 dnech programování (nejprve jsem si dělal prototyp v groovy a pak jsem vytvořil kompletní implementaci v javě) máme řešení, se kterým jsme maximálně spokojeni. Stačí nám zapisovat Hibernate mapování a kód za nás udělá zbytek. My pak už pouze zachytáváme např. DARequiredException, která obsahuje jméno objektu a jeho atributu.

PS. Kód jsem neuváděl, protože není složitý, akorát ho je poměrně dost, takže by se velmi pravděpodobně jednoduchost sdělení hodně zkomplikovala.

1. prosince 2008

Lze testovat aplikaci, která s tím nepočítá

Motivací pro dnešní psaní je 28 czpodcast. Takže zpět k titulku, jde to, ale težko a to je ten problém. Ať už je to metoda, třída, modul nebo celá aplikace, vše musí být navrženo a naimplementováno s ohledem na testovatelnost.

A teď k jádru pudla. Testovatelnost UI je skoro to nejhorší. Ať je to UI webové, swingové a nebo nějaké jiné. Proč? Protože se mění. Většina změn aplikace se projeví v jeho uživatelském rozhraní. Proto se testy musí neustále měnit a modifikovat. Takže není ani tak problém v tom testy napsat, ale udržovat.

Dá se z této situace nějak vybruslit? Nedá, ale dá se řada věcí eliminovat. Tím co se eliminovat dá, je skutečnost, že se rozložení prvků v uživatelském rozhraní mění. Dojde-li k takové změně, pak špatně napsaný test prvek nenajde a nebo najde špatný prvek. Je nutno prvky UI identifikovat spíš než podle umístění (ať fyzického nebo logického) identifikovat podle nějakého logického jména. Tj. ve swingu musíme komponentám dávat jména a v HTML musíme tagům, které pro nás symbolizují komponenty, dávat speciální hodnoty atributům (id nebo class, ale i jiné), tak abychom je v kódu stránky dokázali jednoduše najít.

Pak i když s komponentami trochu hneme, tak nám to nevadí, protože podle tohoto logického názvu je vždy spolehlivě v okně nebo stránce najdeme.

Takže jako musíme komponentu napsat tak, abychom ji byli schopni otestovat, musíme i UI napsat, tak abychom jej otestovali. Rozhodně si tím ušetříme spoustu času a kolikrát si testování de facto umožníme.

14. listopadu 2008

JSF vs. Tapestry - tentokrát AJAX

Zase trochu přileju olej do ohně, tentokrát se podíváme na jednoduchý AJAX příklad. Opět začneme na blogu Jima Driscolla, který napsal A simple Ajax JSF 2.0 example. Při pohledu na tento příklad se opět nemohu ubránit dojmu, že vývoj stránek je v JSF opravdu děs, no posuďte sami (pro jednoduchost ukazuji pouze to co je zajímavé a ne celou stránku):

<h:body>
<h:form id="form1" prependId="false">
<h:outputScript name="ajax.js" library="javax.faces" target="head"/>
<h:outputText id="out1" value="#{count.count}"/>
<br/>
<!-- Increment the counter on the server, and the client -->
<h:commandButton id="button1" value="Count"
onclick="javax.faces.Ajax.ajaxRequest(this, event, {execute: this.id, render: 'out1'}); return false;"/>
<br/>
<!-- Resets the counter -->
<h:commandButton id="reset" value="reset"
onclick="javax.faces.Ajax.ajaxRequest(this, event, {execute:'reset', render: 'out1'}); return false;"
actionListener="#{count.reset}"/>
</h:form>
</h:body>
A jak to samé uděláme v Tapestry nám ukáže Howard L. Ship na svém blogu v příspěvku Tapestry 5 Ajax Screencast. Šablona bude:
<body>
<t:zone t:id="output">${index}</t:zone>
<p>
<t:actionlink t:id="increment" zone="output">increment</t:actionlink>
</p>
<p>
<t:actionlink t:id="reset" zone="output">reset</t:actionlink>
</p>
</body>
A nyní přistupme k Java kódu, ten je v JSF malinko jednodušší, protože definuje pouze beanu s hodnotou počítadla:
@ManagedBean(name = "count")
@SessionScoped
public class Count {
Integer count = 0;

public Integer getCount() {
return count++;
}

public void reset(ActionEvent ae) {
count = 0;
}
}
A nyní ta "složitější" Java třída, tedy Tapestry verze:
public class Index {
@Property
@Persist
private int index;

@InjectComponent
private Zone output;

Object onActionFromIncrement() {
index++;
return output.getBody();
}

Object onActionFromReset() {
index = 0;
return output.getBody();
}
}
Navíc zde máme definováno co se má měnit po provedení AJAX requestu, toť celá komplikace navrch.
Pro mě je to stále jasné, JSF je špatná volba proto, že je hrozně komplikované a těžko mě někdo bude přesvědčovat o opaku.
PS. Letěl okolo mě moc hezký článek o porovnání Wicket a JSF s názvem A Wicket user tries JSF. Ukázky, které jsou tam uvedeny na mě budí dojem, že Tapestry je kombinací toho dobrého z obou těchto světů: jednoduchou Java část z JSF a jednoduché šablony z Wicket.

13. listopadu 2008

Co to je čitelný kód aneb Literate Programming

Obdivuji lidi co mají tu vlastnost, že dokáží naprosto přesně vyjádřit to co ostatní jenom tuší (řada ani to ne). Teď jsem zase narazil na jednoho takového člověka, kterým je Donald E. Knuth, jenž mě dostal svým spiskem o Literate Programming. Nosnou myšlenkou, která mne zaujala je:

Nesnažme se svým programem počítači sdělit co má dělat. Snažme se programem vysvětlit druhým programátorům, co chceme aby počítač dělal.

To je přeci ono. Nepíšeme program jenom proto, aby dělal co má dělat, ale také proto, aby ho dokázal někdo udržovat (to je ten člověk, kterému vysvětluje, co chceme aby počítač dělal).
Již dlouho jsem náš vývojový proces přizpůsoboval této myšlence, možná jsem ji měl někde zakopanou v podvědomí, ale nedokázal jsem ji tak krásně vyjádřit.

3. listopadu 2008

JSF vs. Tapestry - jak jednoduché je dělání komponent

Opět jsem narazil na velmi zajímavý blog, který porovnává složitost vytváření komponent v JSF 2.0 a Tapestry 5 ("Simple" JSF 2.0 Component vs. Tapetry).
Samotného mě takové porovnání již dlouho zajímalo, protože jsem se o JSF přestal zajímat někdy okolo verze 1.0, přišlo mi zkrátka moc složité a komplikované. Navíc mělo veliké problémy na úrovni integrace s JSP (to už je snad minulost). Takže pokud se někdo nechce koukat na originál, pak vězte, že napsat komponentu v JSF, která vypíše text na žlutém pozadí je realizováno následující komponentou:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:composite="http://java.sun.com/jsf/composite">
<head>
<title>This will not be present in rendered output</title>
</head>
<body>

<composite:interface name="yellowOut"
displayName="Very Basic Output Component"
preferred="true"
expert="false"
shortDescription="A basic example">
<composite:attribute name="value" required="false"/>
</composite:interface>

<composite:implementation>
<h:outputText value="#{compositeComponent.attrs.value}" style="background-color: yellow"/>
</composite:implementation>
</body>
</html>
Uf. To je skoro jako těžká dřina. V Tapestry stačí velmi krátká třída:
public class Out {
@Property
@Parameter(defaultPrefix = BindingConstants.LITERAL)
private String value;
}
A ještě kratší šablona:
<span style="background-color: yellow">${value}</span>
Takže já mám zase jasněji v tom, co je lepší používat.

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.