"!$#&% JavaBeans API umožňuje psaní softwarových komponent v programovacím jazyce Java. Komponenta je obecně nějakou částí programového kódu, které je snadné znovupoužívat a se kterou je možno automatizovaně pracovat např. většinou prostřednictvím nějakého vizuálního programovacího nástroje. Komponenta je charakterizována nějakými vlastnostmi, které lze v době návrhu upravovat. Komponenta v Javě je reprezentována obyčejnou třídou, která nemusí mít žádného určitého předka, ani nemusí implementovat žádné speciální rozhraní. Jediné, co se po komponentě v Javě (nazývané Bean) většinou požaduje, je existence konstruktoru bez parametrů a dodržení určité konvence pojmenování metod, protože právě pomocí názvu metody mohou automatizované nástroje rozlišovat, zda jde o nějakou vlastnost, událost či obyčejnou metodu. Proces zkoumání vlastností, událostí a metod nějaké Bean se nazývá introspekce. Introspekci je možné provádět dvěma způsoby: První možnost je dodržení konvencí pojmenování a použití reflexe - pomocí třídy Introspector a její statické metody getbeaninfo(). Té se předá odkaz na objekt Class dané Bean, návratová hodnota je objekt typu BeanInfo, který obsahuje informace o dané Bean. Druhou možností je explicitní poskytnutí informací o Bean prostřednictvím vytvoření instance třídy, která implementuje BeanInfo. Rozhraní BeanInfo implementují třídy poskytující informace o Bean. Metoda getbeandescriptor() vrátí objekt BeanDescriptor poskytující globální informace o Bean, jako její Class, atd. Metoda geteventsetdescriptors() vrací pole objektů EventSetDescriptor, které popisují události generované Bean. Metoda getpropertydescriptors() vrací pole objektů PropertyDescriptor, které slouží k popisu vlastností Bean. Metoda getmethoddescriptors() vrací pole objektů MethodDescriptor, které popisují veřejně přístupné metody Bean. Vlastností objektů Bean je více typů. Nejjednodušší - tzv. jednoduché jsou tvořené jednou hodnotou. Kromě jednoduchých vlastností tvořené jedinou hodnotou lze používat i pole hodnot - tzv. indexované vlastnosti. Vlastnosti lze dále vázat - tyto
vlastnosti upozorní jiné objekty na vzniklou událost pomocí události PropertyChangeEvent. Změnu vlastností lze jinými objekty omezovat - pokud je objekt upozorněn na změnu vlastnosti prostřednictvím PropertyChangeEvent a nová hodnota je nepřijatelná, je možno změnu vetovat pomocí PropertyVetoException. Konvence pro pojmenování metod jsou následující: - Má-li Bean jednoduchou vlastnost se jménem vvv, měla by obsahovat metody public ddd getvvv() public void setvvv(ddd) Kde ddd je datový typ vracený metodou getvvv a zároveň datový typ argumentu metody setvvv. Název vlastnosti a datový typ ddd spolu nesouvisí. Pokud jde o booleovskou vlastnost, je možné (ale ne nutné) nahradit metodu getvvv metodou isvvv. - Pro indexovanou vlastnost Bean obsahuje metody jak pro získání/nastavení celého pole, tak i pro jednotlivé elementy. Např. pro indexovanou vlastnost vvv, ve které je každý element pole typu ddd, bude Bean obsahovat metody: public ddd getvvv(int index) //vrátí element na pozici index public ddd[] getvvv() //vrátí celé pole public void setvvv(int index, ddd value) //nastaví element na pozici index public void setvvv(ddd[] values) //nastaví celé pole -Pro vázané vlastnosti obsahuje Bean get a set metody dle předchozích konvencí podle toho, zda jde o jednoduchou či indexovanou vlastnost. Navíc však obsahuje metody pro registraci objektů, které mají být upozorněny při změně vlastnosti (musí implementovat rozhraní PropertyChangeListener - tj. implementovat metodu propertychange(propertychangeevent evt)). Jsou to: public void addpropertychangelistener(propertychangelistener l) public void removepropertychangelistener(propertychangelistener l) kde l je objekt, kterému bude při změně poslána událost PropertyChangeEvent. Pokud se má nějaké objekty upozorňovat jen na změnu jedné určité vlastnosti, použijí se k tomu metody public void addpropertynamelistener(propertychangelistener l) public void removepropertynamelistener(propertychangelistener l) kde PropertyName je jméno vlastnosti, na jejíž změnu se reaguje.
- Pro vlastnosti s omezením opět existují výše popsané get/set metody, navíc se obdobně jako u vázaných vlastností přidávají metody public void addvetoablechangelistener(vetoablechangelistener l) public void removevetoablechangelistener(vetoablechangelistener l) public void addpropertynamelistener(vetoablechangelistener l) public void removepropertynamelistener(vetoablechangelistener l) kde však objekty, které mají být upozorňovány implementují rozhraní VetoableChangeListener - tedy metodu vetoablechange(propertychangeevent evt). Pokud je nová hodnota vlastnosti nepřípustná, může uvnitř této metody vyvolat objekt výjimku PropertyVetoException, kterou informuje Bean. - Ostatní běžné metody Bean této konvenci nepodléhají, jsou však veřejné - public. Betwixt Ačkoliv asi nejznámější využití architektury JavaBeans je v různých vizuálních vývojových prostředích, kde je díky ní možné tvořit program prostým přetahováním grafických symbolů, které reprezentují příslušné objekty JavaBeans, existují i další možná využití. Velmi zajímavá je například knihovna Betwixt, která slouží pro parametrizovatelné mapování objektů JavaBeans do XML a naopak. K tomuto účelu slouží třídy XMLIntrospector a XMLBeanInfo, které plní obdobné funkce jako Introspector a BeanInfo z java API. Jeden objekt JavaBean lze většinou smysluplně namapovat do XML více možnými způsoby. Např. následující objekt JavaBean, který reprezentuje nějakou osobu, která má jméno a více emailových adres (pro stručnost jsou uvedeny jen metody podstatné pro převod do XML): public class PersonBean { private String name; private String[] emailaddresses; public String getname() { return name; } public String[] getemailaddresses() { return emailaddresses; } }
Jedno možné mapování osoby se jménem Jan a adresami honza@seznam.cz a jan@firma.com, které používá pro ukládání primitivních typů atributy, může vypadat např. takto: <person name='jan'> <emailaddress>honza@seznam.cz</emailaddress> <emailaddress>jan@firma.com</emailaddress> </person> Další možné mapování mapování, které využívá elementy pro všechny vlastnosti, by mohlo např.: <person> <name>jan</name> <email-addresses> <email-address>honza@seznam.cz</email-address> <email-address>jan@firma.com</email-address> </email-addresses> </person> Samozřejmě je možné vymyslet ještě mnoho dalších možností. Knihovna Betwixt umožňuje definovat mapování, kterým se daná JavaBean převede do XML. Pokud není žádné zadáno, použije se defaultní. Je možné nadefinovat jen určitou část mapování, zbytek se poté provede defaultně. Třída XMLIntrospector se nejdříve pokusí najít soubor jménem JavaBeanClassName.betwixt. Použije k tomu stejný ClassLoader, který byl použit na nahrání dané JavaBean. Pokud soubor nalezne, použije ho pro mapování do XML. Pokud soubor neexistuje, použijí se defaultní pravidla. Při použití defaultního nastavení bude výše zmíněná JavaBean převedena do následujícího XML kódu: <PersonBean id="1"> <emailaddresses> <String>honza@seznam.cz</String> <String>jan@firma.cz</String> </emailaddresses> <name>jan</name>
</PersonBean> Tento převod lze uskutečnit tímto jednoduchým způsobem: StringWriter outputwriter = new StringWriter(); (new BeanWriter(outputWriter)).write(person); kde person je instance třídy PersonBean a v proměnné outputwriter bude uložen její XML popis. Na konzoli lze poté vypsat voláním: System.out.println(outputWriter.toString()); Pokud bychom chtěli např provést přejmenování názvů elementů a jméno osoby ukládat jako atribut vnějšího elementu, můžeme použít soubor PersonBean.betwixt s následující strukturou: <?xml version='1.0' encoding='utf-8'?> <!-- pokud bychom chtěli implicitní převádět primitivní typy do atributů, mohli bychom to udělat přidáním tohoto atributu do elementu <info>: primitivetypes='attribute' --> <info> <!-- vnější element bude mít název 'osoba' --> <element name='osoba'> <!-- vnější element bude mít atribut 'jmeno', jeho hodnota bude hodnota vlastnosti name --> <attribute name='jmeno' property='name'/> <!-- další element bude mít název 'emailove_adresy' --> <element name='emailove_adresy'> <!-- další element bude mít název 'adresa', uvnitř elementu bude hodnota vlastnosti emailaddresses --> <element name='adresa' property='emailaddresses'/> <!-- pokud by JavaBean měla nějaké další vlastnosti, které by se měli zpracovat defaultním způsobem, stačilo by přidat element <adddefaults/> --> </element> </element> </info> Pokud tento soubor umístíme do místa, kde ho Class loader, který nahrál třídu
PersonBean, najde, dostaneme převodem následující XML schema: <osoba jmeno="jan"> <emailove_adresy> <adresa>honza@seznam.cz</adresa> <adresa>jan@firma.cz</adresa> </emailove_adresy> </osoba> Knihovna Betwixt nabízí ještě více užitečných vlastností, jejichž podrobnější popis lze najít na internetových stránkách projektu http://jakarta.apache.org/betwixt. Java API pro dlouhodobou persistenci Ve standardním javovském balíčku java.beans přibylo od JDK verze 1.4 tzv. API pro dlouhodobou perzistenci JavaBean. Třídy v něm umožňují zápis a čtení objektů JavaBean do/z textové reprezentace. Třída XMLEncoder umožňuje vytvořit XML reprezentaci objektu JavaBean a pomocí třídy XMLDecoder je možné z XML reprezentace vytvořit příslušný JavaBean objekt. Použití tohoto API je oproti knihovně Betwixt o něco jednodušší. Narozdíl od knihovny Betwixt však toto API nenabízí prakticky žádné možnosti přizpůsobení toho, jak se bude provádět mapování a výsledná XML reprezentace nevypadá tak "pěkně". Pro porovnání ukázka, jak bude vypadat XML reprezentace výše zmiňovaného objektu PersonBean, pokud bude pomocí tohoto API: <java version="1.4.2" class="java.beans.xmldecoder"> <object class="personbean"> <void property="emailaddresses"> <array class="java.lang.string" length="2"> <void index="0"> <string>honza@seznam.cz</string> <void index="1"> <string>jan@firma.cz</string>
</array> <void property="name"> <string>jan</string> </object> </java> Javovský kód, použitý pro vygenerování tohoto schematu: ByteArrayOutputStream ba = new ByteArrayOutputStream(); XMLEncoder e = new XMLEncoder(new BufferedOutputStream(ba)); e.writeobject(person); e.close(); System.out.print(ba.toString()); kde person je instance výše popsané třídy PersonBean. '<(*<+=,/.10>29461(-8?: ')(*+-,/.10325467(98;: [1] The Java Tutorial URL: http://java.sun.com/docs/books/tutorial [2] Bruce Eckel Myslíme v jazyku Java - knihovna zkušeného programátora.. Grada Publishing. 2001. 80-247-0027-1. [3] W3C (2000): Extensible Markup Language (XML) 1.0 (Second Edition); W3C Recommendation URL: http://www.w3.org/tr/rec-xml [4] Simple API for XML (SAX); Internetové stránky projektu SAX URL: http://www.saxproject.org/ [5] W3C (2000): Document Object Model (DOM) Level 2 Core Specification Version 1.0; W3C Recommendation URL: http://www.w3.org/tr/dom-level-2-core/ [6] Apache Jakarta project - Betwixt URL: http://jakarta.apache.org/commons/betwixt