Zbytečné upozornění — JDK 5.0 Beta 3
Na předchozí pojednání navazuje krátká zprávička — Sun včera zveřejnil třetí betaverzi JDK 5.0. Tato beta se od předchozí liší tím, že byla mnohem méně testována, je vydána v méně podobách ("no JRE bundles, Solaris packages, Linux RPMs, or Windows install-from-the-web bundles") a s minimem dokumentace.
Takže pokud nemáte obzvlášť dobrý důvod, můžete tuto informaci s klidem ignorovat 
Anotace v J2SDK 1.5
Když se před časem začaly objevovat první zmínky o chystaných změnách v jazyce Java, které s sebou přinese nová verze SDK, většinou byla řeč o generických datových typech, autoboxingu/unboxingu a podobně, kdežto poměrně závažnější novinka — označovaná častěji pojmem metadata — byla odbyta příkladem na generování rozhrání pro RMI.
Uteklo pár měsíců, vyšlo pár dalších alfa až beta verzí, dokumentace utěšeně bobtná, místo metadata se říká anotace a především (aspoň pro účely tohoto povídání ;) jsem si našel čas "jednapětku" trochu vyzkoušet.
Takže co všechno se dá s anotacemi dělat a k čemu to je dobré?
Anotace nám částečně zjednodušují různá nejmenovaná divoká API, která by pro každý objekt nejraději šest rozhraní a nadto pár XML souborů, částečně dávají pevný rámec pro vytváření informací o prvcích kódu — to známe třeba Javadoc značku @deprecated nebo modifikátor transient (nedělá vlastně nic, jen upozorňuje, že takto označenou položku nechceme uchovávat v serializované podobě objektu).
V základních bodech:
- anotace mohou být připojeny k jednotlivým prvkům programu v jazyce Java: deklaraci typu (tj. třídě, rozhraní a nově vlastně i výčtovému typu, balíku, metodě (včetně konstruktoru), proměnné (nejen pole třídy, ale i lokální proměnné) a konečně i vlastní definici anotace
- anotace obsahuje název anotace a případně nějaké ty parametry
- anotace mohou být zpracovávány během procesu sestavování aplikace (mohou být podle nich třeba generovány další užitečné soubory), mohou být za běhu čteny pomocí reflexe a nebo mohou být jen tak pro srandu králíkům
- anotace nám samozřejmě nepadají z nebe, ale musíme si je naprogramovat, stejně jako programujeme třídy či rozhraní
- a když už nějakou tu anotaci vytváříme, rovnou můžeme určit omezení jako třeba "jaké elementy jazyka Java mohou být tímto anotovány" nebo "jak moc chceme anotace uchovávat"
Z těchto úvodních čtyř bodů snad máme hrubý obraz o tom, co takové anotace obnášejí, teď je čas si názorně ukázat, jak anotace vytvářet a používat.
Vsuvka: je sice pravda, že většina programátorů anotace bude pouze používat, na druhou stranu ale očekávejte, že vzhledem k poměrné jednoduchosti anotačních deklarací může jejich dokumentace být z velké části založena na tom, že se zobrazí zdrojový kód.
Vsuvka č.2: omluvte prosím zvýšený výskyt pojmů končících na "-ace".
Deklarace anotace
Syntaxi, kterou při deklarování naší anotace použijeme, názorně demonstruje následující ukázka převzatá z květnového článku J2SE 1.5 in a Nutshell:
@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(java.lang.annotation.ElementType.METHOD)
public @interface debug {
boolean devbuild() default false;
int counter();
}
Stručný popis:
- vlastní anotaci deklarujeme pomocí klíčového slova
@interface(ve výše uvedeném příkladě jsme deklarovali anotaci @debug) - stejně jako u jiných typů, i anotaci můžeme deklarovat v samostatném souboru či zároveň s definicí jiné třídy a můžeme k ní určovat přístup pomocí public apod.
- anotace může být doplněna tzv. "metaanotacemi" definovanými v balíku java.lang.annotation; — tyto metaanotace určují, jak dlouho má anotace vydržet (Retention) — zda se má zahodit při kompilaci nebo má být přístupná za běhu prostřednictvím reflexe či má být zachována bez potřeby přístupu reflexí; další anotace — Target — pak určuje, ke kterému prvku jazyka (třída, metoda apod.) se anotace vztahue
- anotace může obsahovat parametry (v našem případě boolean "devbuild" a int "counter"), nepovinným parametrům se klíčovým slovem
defaultnastavuje výchozí hodnota - parametry anotací mohou být pouze primitivní typy, řetězce nebo výčtové typy (enum)
Víme tedy, že jsme si vytvořili veřejně přístupnou anotaci určenou pro anotování metod, s tím, že tuto anotaci je možné číst prostřednictvím reflexe.
Takto deklarovanou anotaci ve vlastním kódu můžeme použít následujícím způsobem:
public class MetaTest {
@debug(devbuild=production,counter=1) public void testMethod() {
}
}
Co teď s tím? Anotace můžeme pročítat za běhu pomocí reflexe a nebo je zpracovat za buildu (a třeba z nich přitom něco vygenerovat). To si zaslouží dvě samostatné kapitolky:
Čtení anotací pomocí reflexe
Všechny prvky jazyka Java, které mohou být opatřeny anotacemi, pochopitelně získaly nové "reflexní" metody pro čtení anotací. A aby v tom nebyl guláš, tyto metody byly shrnuty v novém rozhraní AnnotatedElement, které je implementováno již důvěrně znamými třídami AccessibleObject, Class, Constructor, Field, Method a Package.
Popis metod, které toto rozhraní přineslo, si najdete v odkazovaném javadocu, zde jen jednoduchý příklad z téhož zdroje jako byl příklad předchozí:
public class MetaTest {
final boolean production=true;
@debug(devbuild=production,counter=1) public void testMethod() { }
public static void main(String args) {
MetaTest mt = new MetaTest();
try {
Annotation a = mt.getClass().getMethod("testMethod").getAnnotations();
for (int i=0; i
}
} catch(NoSuchMethodException e) {
System.out.println(e);
}
}
}
V příkladu jsme použili již zmíněnou anotaci @debug a anotovanou metodu testMethod(). V metodě main() pak projíždíme v cyklu použité anotace a vypisujeme je na standardní výstup. Pokud bychom nebyli čtenář Jabloku, ale třeba EJB 3.0 kontejner, asi bychom dělali se zjištěnými anotacemi dělali něco zajímavějšího.
Zpracovávání anotací během sestavování aplikace
Pokud chceme anotace využít způsobem řekněme "docletovým", musíme si vzít na pomoc novou utilitku apt neboli annotation processing tool (nezaměňovat s debianí systémovou utilitkou).
Více se dočtete přimo na stránkách aptu, protože jsem s tímto produktem zatím nic generovat nezkoušel, nebudu dělat chytrého a nechám bližší seznámení na jindy.
K čemu nám to všechno bude?
Na závěr jedna motivační citace z early draftu nedávno zmiňované specifikace EJB 3.0:
@Target (TYPE) @Retention (RUNTIME)
public @interface Entity {
String name() default "";
EntityType entityType() default CMP;
AccessType access() default PROPERTY;
int version() default 3;
}
Jasné, ne?
Tomcat 5 a temp adresář
Právě jsem úspěšně přemluvil Tomcat 5, aby mé aplikaci dovolil detekci rozměrů obrázku, které do ní uživatel uploaduje. V první chvíli na mě totiž kocour Tomáš zkoušel nekalé triky:
javax.imageio.IIOException: Can't create cache file!
at javax.imageio.ImageIO.createImageInputStream(ImageIO.java:333)
at javax.imageio.ImageIO.read(ImageIO.java:1305)
at cms2.web.util.ImageInfo.(ImageInfo.java:21)
at cms2.web.form.ImageForm.setFileBig(ImageForm.java:275)
...
Caused by: java.io.IOException: No such file or directory
at java.io.UnixFileSystem.createFileExclusively(Native Method)
at java.io.File.checkAndCreate(File.java:1313)
at java.io.File.createTempFile(File.java:1401)
at javax.imageio.stream.FileCacheImageInputStream.(FileCacheImageInputStream.java:70)
at com.sun.imageio.spi.InputStreamImageInputStreamSpi.createInputStreamInstance(InputStreamImageInputStreamSpi.java:51)
at javax.imageio.ImageIO.createImageInputStream(ImageIO.java:329)
...
Nechtěl jsem mu věřit, že bych neměl na disku adresář "/tmp" (viz. java.io.File.createTempFile()), nedal jsem se zastrašit a našel jsem ve spuštěcím skriptu Tomcatu takovouhle věc: -Djava.io.tmpdir="$CATALINA TMPDIR". Pak jen stačilo zjistit, že kýžený $CATALINA TMPDIR má být adresář "$CATALINA BASE"/temp a příslušný adresář vytvořit. Z nějakého důvodu se v instalaci Tomcata tento adresář (mmch ani adresář logs) nevyskytuje.
Nemůžu vyloučit, že bych se o tom dočetl v nějaké zastrčené kapitole dokumentace, ale přesto:
Ošklivý kocour! Fuj, fuj!
Hřiště pro Javu 1.5 (vlastně už jen 5)
Na adrese http://zamples.com/JspExplorer/samples/index.jsp si můžete krom jineho vyzkoušet fragmenty kódu demonstrující nejprofláknutější novinky v jazyce Java, které přináší verze v novém číslování označená jako 5.
Aktualizace: opravena adresa na zaklade laskaveho upozorneni ctenare "deivid zavinac lycos.com".